White-label ticketing · launch a branded box office in minutes
All integrations

Embed TicketWave on Any Website

One script tag. Any website. Under 30KB.

Quick Start

The 30-second version. Paste this HTML wherever you want tickets to appear:

<div id="ticketwave-widget" data-client="YOUR-SLUG" data-event="YOUR-EVENT"></div>
<script src="https://www.ticketwavehq.com/widget/ticketwave.js" async></script>

Replace YOUR-SLUG with your venue slug and YOUR-EVENTwith your event slug. That's it.

Full Configuration Reference

Every data attribute you can add to the widget container. Only data-client and data-event are required.

AttributeTypeDefaultDescription
data-clientstringRequiredYour venue slug from the dashboard
data-eventstringRequiredEvent slug (found on the event detail page under Share & Embed)
data-api-urlstringhttps://www.ticketwavehq.com/apiAPI endpoint. Only change if self-hosting.
data-theme"light" | "dark""light"Widget colour scheme. Matches light or dark backgrounds.
data-accent-colorhex string"#000000"Primary accent colour for buttons and interactive elements.
data-button-textstring"Buy Tickets"CTA button label. Common alternatives: "Book Now", "Get Tickets", "Reserve".
data-localestring"en"Language for all widget text. Supported: en, es, pt, el, fr, de, it.
data-show-description"true" | "false""true"Show or hide the event description inside the widget.
data-show-venue"true" | "false""true"Show or hide venue information (name, address) inside the widget.

Full example with all options

<!-- TicketWave Ticket Widget -->
<div id="ticketwave-widget"
     data-client="YOUR-VENUE-SLUG"
     data-event="YOUR-EVENT-SLUG"
     data-api-url="https://www.ticketwavehq.com/api"
     data-theme="light"
     data-accent-color="#FF6B4A"
     data-button-text="Book Now"
     data-locale="en"
     data-show-description="true"
     data-show-venue="true"
     style="max-width: 480px; margin: 0 auto;">
</div>
<script src="https://www.ticketwavehq.com/widget/ticketwave.js" async></script>

Framework Examples

React / Next.js

A TypeScript component with proper useEffect cleanup. Loads the script once and removes container content on unmount to prevent memory leaks in SPAs.

'use client'; // Next.js App Router — remove if using Pages Router

import { useEffect, useRef } from 'react';

interface TicketWaveWidgetProps {
  client: string;
  event: string;
  theme?: 'light' | 'dark';
  accentColor?: string;
  buttonText?: string;
  locale?: string;
  showDescription?: boolean;
  showVenue?: boolean;
}

export function TicketWaveWidget({
  client,
  event,
  theme = 'light',
  accentColor,
  buttonText,
  locale = 'en',
  showDescription = true,
  showVenue = true,
}: TicketWaveWidgetProps) {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    // Load the widget script once
    const existingScript = document.querySelector(
      'script[src="https://www.ticketwavehq.com/widget/ticketwave.js"]'
    );

    if (!existingScript) {
      const script = document.createElement('script');
      script.src = 'https://www.ticketwavehq.com/widget/ticketwave.js';
      script.async = true;
      document.body.appendChild(script);
    }

    // Cleanup: remove the container content on unmount
    return () => {
      if (containerRef.current) {
        containerRef.current.innerHTML = '';
      }
    };
  }, []);

  return (
    <div
      ref={containerRef}
      id={`ticketwave-widget-${event}`}
      data-client={client}
      data-event={event}
      data-api-url="https://www.ticketwavehq.com/api"
      data-theme={theme}
      {...(accentColor ? { 'data-accent-color': accentColor } : {})}
      {...(buttonText ? { 'data-button-text': buttonText } : {})}
      data-locale={locale}
      data-show-description={String(showDescription)}
      data-show-venue={String(showVenue)}
      style={{ maxWidth: 480, margin: '0 auto' }}
    />
  );
}

// Usage:
// <TicketWaveWidget
//   client="my-venue"
//   event="summer-party-2026"
//   theme="dark"
//   accentColor="#FF6B4A"
//   buttonText="Get Tickets"
// />
Vue.js

A Vue 3 component using the Composition API and <script setup> syntax. Manages the widget script lifecycle with onMounted and onUnmounted.

<template>
  <div
    :id="'ticketwave-widget-' + event"
    :data-client="client"
    :data-event="event"
    data-api-url="https://www.ticketwavehq.com/api"
    :data-theme="theme"
    :data-accent-color="accentColor"
    :data-button-text="buttonText"
    :data-locale="locale"
    :data-show-description="showDescription"
    :data-show-venue="showVenue"
    style="max-width: 480px; margin: 0 auto;"
  />
</template>

<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue';

const props = withDefaults(defineProps<{
  client: string;
  event: string;
  theme?: 'light' | 'dark';
  accentColor?: string;
  buttonText?: string;
  locale?: string;
  showDescription?: string;
  showVenue?: string;
}>(), {
  theme: 'light',
  locale: 'en',
  showDescription: 'true',
  showVenue: 'true',
});

let script: HTMLScriptElement | null = null;

onMounted(() => {
  const existing = document.querySelector(
    'script[src="https://www.ticketwavehq.com/widget/ticketwave.js"]'
  );
  if (!existing) {
    script = document.createElement('script');
    script.src = 'https://www.ticketwavehq.com/widget/ticketwave.js';
    script.async = true;
    document.body.appendChild(script);
  }
});

onUnmounted(() => {
  // Only remove if we added it and no other instances exist
  if (script && script.parentNode) {
    script.parentNode.removeChild(script);
  }
});
</script>

<!-- Usage: -->
<!-- <TicketWaveWidget client="my-venue" event="summer-party-2026" /> -->
Vanilla JavaScript

Programmatic initialization for when you need dynamic control — useful for loading events based on user interaction, URL parameters, or API responses.

// Programmatic initialization — useful when you need dynamic control
function loadTicketWave(containerId, config) {
  const container = document.getElementById(containerId);
  if (!container) return;

  // Set data attributes from config
  container.setAttribute('data-client', config.client);
  container.setAttribute('data-event', config.event);
  container.setAttribute('data-api-url', config.apiUrl || 'https://www.ticketwavehq.com/api');

  if (config.theme) container.setAttribute('data-theme', config.theme);
  if (config.accentColor) container.setAttribute('data-accent-color', config.accentColor);
  if (config.buttonText) container.setAttribute('data-button-text', config.buttonText);
  if (config.locale) container.setAttribute('data-locale', config.locale);

  // Load the script (idempotent — safe to call multiple times)
  if (!document.querySelector('script[src*="ticketwave.js"]')) {
    const script = document.createElement('script');
    script.src = 'https://www.ticketwavehq.com/widget/ticketwave.js';
    script.async = true;
    document.body.appendChild(script);
  }
}

// Usage:
loadTicketWave('ticket-container', {
  client: 'my-venue',
  event: 'summer-party-2026',
  theme: 'dark',
  accentColor: '#FF6B4A',
  buttonText: 'Get Tickets',
});
iFrame Fallback

Use the iFrame method when your CMS or hosting platform blocks external JavaScript. The iFrame provides complete CSS and JS isolation — the widget loads in its own context with no interference from your page.

<!-- iFrame fallback — use when your CMS blocks external scripts -->
<iframe
  src="https://www.ticketwavehq.com/embed/YOUR-VENUE-SLUG/YOUR-EVENT-SLUG"
  width="100%"
  height="600"
  frameborder="0"
  style="border: none; max-width: 480px; margin: 0 auto; display: block;"
  title="Buy Tickets — Event Name"
  loading="lazy"
  allow="payment">
</iframe>

Note: The iFrame method does not support data attributes. To customise the widget (theme, accent colour, etc.), append query parameters to the URL instead: ?theme=dark&accent=FF6B4A

Multiple Events on One Page

Add multiple widget containers with different data-event values. Each container must have a unique id. The script tag only needs to appear once.

<!-- Event 1 -->
<div id="ticketwave-widget-1"
     data-client="my-venue"
     data-event="summer-party-2026"
     data-api-url="https://www.ticketwavehq.com/api"
     style="max-width: 480px; margin: 0 auto 2rem;">
</div>

<!-- Event 2 -->
<div id="ticketwave-widget-2"
     data-client="my-venue"
     data-event="winter-gala-2026"
     data-api-url="https://www.ticketwavehq.com/api"
     style="max-width: 480px; margin: 0 auto 2rem;">
</div>

<!-- Only include the script ONCE — it handles all widget containers -->
<script src="https://www.ticketwavehq.com/widget/ticketwave.js" async></script>

Responsive Behaviour

The widget is responsive by default. It fills 100% of its container width and adapts its layout for mobile, tablet, and desktop screen sizes. To control the maximum width, wrap it in a container or use the style attribute:

<!-- Responsive container with max-width -->
<div style="max-width: 480px; margin: 0 auto; padding: 1rem;">
  <div id="ticketwave-widget"
       data-client="my-venue"
       data-event="summer-party-2026"
       data-api-url="https://www.ticketwavehq.com/api">
  </div>
</div>
<script src="https://www.ticketwavehq.com/widget/ticketwave.js" async></script>

320px+

Mobile layout

Stacked, single column

768px+

Tablet layout

Compact, optimised spacing

1024px+

Desktop layout

Full features, expanded view

Security

  • The widget loads from our CDN over HTTPS
  • It runs in an isolated scope (shadow DOM) and does not access your page's DOM beyond its container
  • All payment processing happens via Stripe's PCI-compliant infrastructure
  • No cookies are set on your domain
  • CSP: add https://www.ticketwavehq.com to script-src and connect-src

Performance

  • The widget bundle is under 30KB gzipped
  • It loads asynchronously (async attribute) and does not block your page rendering
  • Zero impact on your Core Web Vitals (LCP, FID, CLS)
  • Served from a global CDN with edge caching
  • Event data is fetched lazily after the widget is visible

Live Preview

This is what the widget looks like when embedded on a page:

yoursite.com/tickets

Get Your Tickets

General Admission

$25.00

0

VIP Access

$75.00

0
Book Now

Technical FAQ

Does the widget work on single-page applications (SPAs)?

Yes. The script detects dynamically inserted widget containers. For React and Next.js apps, use the React component example above to manage the script lifecycle properly. For Vue, use the Vue component example.

Is the widget responsive?

Yes. The widget is fully responsive and adapts to its container width. On mobile devices it renders a mobile-optimised layout with touch-friendly controls. Set max-width on the container to control the maximum size.

Can I style the widget with my own CSS?

The widget renders inside a shadow DOM to prevent CSS conflicts. Use the data-theme and data-accent-color attributes to customise appearance. For advanced styling, contact support for custom CSS injection options.

What about Content Security Policy (CSP)?

If your site uses a strict CSP, add https://www.ticketwavehq.com to your script-src and connect-src directives. The widget only loads resources from this single domain.

Does the iFrame embed support postMessage events?

Yes. The iFrame emits postMessage events for key actions like ticket selection and purchase completion. Listen for messages from the ticketwavehq.com origin to integrate with your own analytics or UI.

Can I embed tickets for multiple events on one page?

Yes. Add multiple widget containers with different data-event values and unique IDs. The script tag only needs to be included once — it initialises all widget containers on the page automatically.

Explore other integrations

Ready to embed tickets on your site?

Create your free account, grab the embed code from your dashboard, and start selling in minutes.