Embedding

Embed booking pages and widgets in your application.

Overview

Embed Transactional Calendar booking experiences directly into your website or application. Multiple embedding options available for different use cases.

Embedding Options

MethodDescriptionBest For
Inline EmbedBooking widget in your pageFull integration
PopupModal overlayCall-to-action buttons
LinkDirect to hosted pageEmail, external links
React ComponentNative React integrationReact applications

Inline Embed

Basic HTML Embed

<!-- Container for booking widget -->
<div id="transactional-booking"></div>
 
<!-- Load embed script -->
<script src="https://cdn.usetransactional.com/sdk/calendar.min.js"></script>
 
<script>
  TransactionalCalendar.init({
    elementId: 'transactional-booking',
    eventTypeSlug: 'demo',
    organizationSlug: 'your-org',
  });
</script>

With Options

<script>
  TransactionalCalendar.init({
    elementId: 'transactional-booking',
    eventTypeSlug: 'demo',
    organizationSlug: 'your-org',
 
    // Theming
    theme: 'light', // 'light', 'dark', 'auto'
    primaryColor: '#0066cc',
    borderRadius: 8,
 
    // Prefill attendee info
    prefill: {
      name: 'John Doe',
      email: 'john@example.com',
      responses: {
        company: 'Acme Corp',
      },
    },
 
    // Hide elements
    hideEventTypeDetails: false,
    hideHostInfo: false,
 
    // Callbacks
    onBookingComplete: (booking) => {
      console.log('Booking created:', booking);
      trackConversion(booking);
    },
    onSlotSelected: (slot) => {
      console.log('Slot selected:', slot);
    },
    onError: (error) => {
      console.error('Booking error:', error);
    },
  });
</script>

Trigger from Button

<button onclick="openBookingPopup()">Schedule a Demo</button>
 
<script src="https://cdn.usetransactional.com/sdk/calendar.min.js"></script>
 
<script>
  function openBookingPopup() {
    TransactionalCalendar.popup({
      eventTypeSlug: 'demo',
      organizationSlug: 'your-org',
      theme: 'light',
      onBookingComplete: (booking) => {
        console.log('Booked:', booking);
      },
    });
  }
</script>

Auto-trigger on Page Load

<script>
  // Show popup after 30 seconds on page
  setTimeout(() => {
    if (!sessionStorage.getItem('booking_popup_shown')) {
      TransactionalCalendar.popup({
        eventTypeSlug: 'demo',
        organizationSlug: 'your-org',
      });
      sessionStorage.setItem('booking_popup_shown', 'true');
    }
  }, 30000);
</script>

React Integration

Install Package

npm install @usetransactional/react

Inline Component

import { CalendarEmbed } from '@usetransactional/react';
 
function BookingPage() {
  const handleComplete = (booking) => {
    console.log('Booking created:', booking);
    router.push('/thank-you');
  };
 
  return (
    <div className="booking-container">
      <h1>Schedule a Meeting</h1>
      <CalendarEmbed
        eventTypeSlug="demo"
        organizationSlug="your-org"
        theme="light"
        primaryColor="#0066cc"
        prefill={{
          name: user?.name,
          email: user?.email,
        }}
        onBookingComplete={handleComplete}
      />
    </div>
  );
}
import { CalendarModal } from '@usetransactional/react';
import { useState } from 'react';
 
function BookingButton() {
  const [isOpen, setIsOpen] = useState(false);
 
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Schedule a Demo
      </button>
 
      <CalendarModal
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        eventTypeSlug="demo"
        organizationSlug="your-org"
        onBookingComplete={(booking) => {
          setIsOpen(false);
          showSuccessMessage(booking);
        }}
      />
    </>
  );
}

Next.js Dynamic Import

import dynamic from 'next/dynamic';
 
const CalendarEmbed = dynamic(
  () => import('@usetransactional/react').then(mod => mod.CalendarEmbed),
  { ssr: false }
);
 
export default function BookingPage() {
  return (
    <CalendarEmbed
      eventTypeSlug="demo"
      organizationSlug="your-org"
    />
  );
}
const bookingUrl = `https://calendar.usetransactional.com/${orgSlug}/${eventTypeSlug}`;

With Prefill

const params = new URLSearchParams({
  name: 'John Doe',
  email: 'john@example.com',
  'responses[company]': 'Acme Corp',
});
 
const bookingUrl = `https://calendar.usetransactional.com/${orgSlug}/${eventTypeSlug}?${params}`;

For Specific Host

const bookingUrl = `https://calendar.usetransactional.com/${orgSlug}/${eventTypeSlug}?host=${hostSlug}`;

iFrame Embed

For complete isolation:

<iframe
  src="https://calendar.usetransactional.com/your-org/demo"
  width="100%"
  height="700"
  frameborder="0"
  allow="payment"
></iframe>

Responsive iFrame

<div style="position: relative; padding-bottom: 100%; height: 0; overflow: hidden;">
  <iframe
    src="https://calendar.usetransactional.com/your-org/demo"
    style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"
    frameborder="0"
  ></iframe>
</div>

Customization

Theme Options

TransactionalCalendar.init({
  elementId: 'booking',
  eventTypeSlug: 'demo',
  organizationSlug: 'your-org',
 
  // Colors
  primaryColor: '#0066cc',
  backgroundColor: '#ffffff',
  textColor: '#333333',
 
  // Typography
  fontFamily: 'Inter, sans-serif',
 
  // Layout
  borderRadius: 8,
  padding: 24,
 
  // Theme
  theme: 'light', // 'light', 'dark', 'auto'
});

Custom CSS

<style>
  .transactional-calendar {
    --tx-primary: #0066cc;
    --tx-primary-hover: #0052a3;
    --tx-background: #ffffff;
    --tx-text: #333333;
    --tx-text-muted: #666666;
    --tx-border: #e0e0e0;
    --tx-border-radius: 8px;
  }
 
  .transactional-calendar .slot-button {
    /* Custom slot styling */
  }
 
  .transactional-calendar .confirm-button {
    /* Custom button styling */
  }
</style>

CSS Classes Reference

ClassElement
.tx-calendarContainer
.tx-headerHeader section
.tx-event-titleEvent name
.tx-host-infoHost details
.tx-date-pickerCalendar picker
.tx-time-slotsTime slot list
.tx-slot-buttonIndividual slot
.tx-formAttendee form
.tx-inputForm inputs
.tx-confirm-btnConfirm button

Prefilling Data

From URL Parameters

// Your app generates link with parameters
const bookingLink = `/book/demo?name=${encodeURIComponent(user.name)}&email=${encodeURIComponent(user.email)}`;

From JavaScript

TransactionalCalendar.init({
  elementId: 'booking',
  eventTypeSlug: 'demo',
  organizationSlug: 'your-org',
  prefill: {
    name: userData.name,
    email: userData.email,
    phone: userData.phone,
    responses: {
      company: userData.company,
      role: userData.role,
    },
  },
});

Dynamic Prefill (React)

function BookingPage() {
  const { user } = useAuth();
 
  return (
    <CalendarEmbed
      eventTypeSlug="demo"
      organizationSlug="your-org"
      prefill={{
        name: user?.name,
        email: user?.email,
        // Lock email if authenticated
        lockEmail: !!user,
      }}
    />
  );
}

Event Callbacks

TransactionalCalendar.init({
  elementId: 'booking',
  eventTypeSlug: 'demo',
  organizationSlug: 'your-org',
 
  // Lifecycle callbacks
  onReady: () => {
    console.log('Calendar widget loaded');
  },
 
  onDateSelected: (date) => {
    console.log('Date selected:', date);
    analytics.track('date_selected', { date });
  },
 
  onSlotSelected: (slot) => {
    console.log('Slot selected:', slot);
    analytics.track('slot_selected', slot);
  },
 
  onFormSubmitted: (data) => {
    console.log('Form submitted:', data);
  },
 
  onBookingComplete: (booking) => {
    console.log('Booking created:', booking);
    analytics.track('booking_complete', {
      bookingId: booking.uid,
      eventType: booking.eventType,
    });
    // Redirect or show success
  },
 
  onBookingError: (error) => {
    console.error('Booking failed:', error);
    analytics.track('booking_error', { error: error.message });
  },
});

Responsive Design

The embed automatically adapts to container width:

/* Mobile-first responsive container */
.booking-container {
  width: 100%;
  max-width: 600px;
  margin: 0 auto;
}
 
@media (min-width: 768px) {
  .booking-container {
    max-width: 800px;
  }
}

Security

Allowed Domains

Configure which domains can embed your calendar:

await client.calendar.settings.update({
  embedding: {
    allowedDomains: [
      'yourapp.com',
      '*.yourapp.com',
      'localhost:3000', // For development
    ],
  },
});

Content Security Policy

If using CSP, add:

frame-src https://calendar.usetransactional.com;
script-src https://cdn.usetransactional.com;

Next Steps