Skip to main content
The vanilla JavaScript embed snippet is the lightest way to add Cal.com booking functionality to any website. It works with any tech stack and requires no build tools or dependencies.

Installation

Add the embed snippet to your HTML page. This is all you need to get started:
<script>
  (function (C, A, L) {
    let p = function (a, ar) { a.q.push(ar); };
    let d = C.document;
    C.Cal = C.Cal || function () {
      let cal = C.Cal;
      let ar = arguments;
      if (!cal.loaded) {
        cal.ns = {};
        cal.q = cal.q || [];
        d.head.appendChild(d.createElement("script")).src = A;
        cal.loaded = true;
      }
      if (ar[0] === L) {
        const api = function () { p(api, arguments); };
        const namespace = ar[1];
        api.q = api.q || [];
        if (typeof namespace === "string") {
          cal.ns[namespace] = cal.ns[namespace] || api;
          p(cal.ns[namespace], ar);
          p(cal, ["initNamespace", namespace]);
        } else p(cal, ar);
        return;
      }
      p(cal, ar);
    };
  })(window, "https://app.cal.com/embed/embed.js", "init");
</script>
Replace https://app.cal.com with your Cal.com instance URL if you’re self-hosting.

Quick Start Examples

Inline Embed

Embed the calendar directly in your page:
<!-- 1. Add the embed snippet (shown above) -->

<!-- 2. Create a container -->
<div id="my-cal-inline" style="width:100%;height:100%;overflow:scroll"></div>

<!-- 3. Initialize the embed -->
<script>
  Cal("init");
  Cal("inline", {
    elementOrSelector: "#my-cal-inline",
    calLink: "organization/event-type"
  });
</script>
Open the calendar in a modal when a button is clicked:
<!-- 1. Add the embed snippet -->

<!-- 2. Add a button with data attributes -->
<button 
  data-cal-link="organization/event-type"
  data-cal-config='{"layout":"month_view","theme":"dark"}'
>
  Book a meeting
</button>

<!-- 3. Initialize -->
<script>
  Cal("init");
</script>

Floating Button

Add a persistent floating button to your page:
<!-- 1. Add the embed snippet -->

<!-- 2. Initialize with floating button -->
<script>
  Cal("init");
  Cal("floatingButton", {
    calLink: "organization/event-type",
    buttonText: "Book a meeting",
    buttonPosition: "bottom-right",
    config: {
      theme: "dark"
    }
  });
</script>

API Reference

Initialization

Always initialize Cal before using it:
Cal("init", {
  origin: "https://cal.com",  // Optional: Cal.com instance URL
  debug: false,                // Optional: Enable debug logging
  uiDebug: false              // Optional: Enable UI debug mode
});

Inline Method

Embed the calendar inline in a container:
Cal("inline", {
  elementOrSelector: "#my-cal-inline",  // Required: CSS selector or element
  calLink: "organization/event-type",   // Required: Booking link
  config: {                              // Optional: Configuration
    name: "John Doe",
    email: "[email protected]",
    notes: "Initial discussion",
    theme: "dark",
    layout: "month_view"
  }
});
Open the calendar in a modal programmatically:
Cal("modal", {
  calLink: "organization/event-type",
  config: {
    theme: "dark",
    layout: "month_view"
  }
});

Floating Button Method

Create a floating action button:
Cal("floatingButton", {
  calLink: "organization/event-type",
  buttonText: "Book a meeting",          // Button text
  buttonPosition: "bottom-right",         // Position: bottom-right, bottom-left, etc.
  config: {                               // Optional: Configuration
    theme: "dark"
  }
});

UI Method

Update UI configuration dynamically:
Cal("ui", {
  theme: "dark",
  styles: {
    branding: {
      brandColor: "#000000"
    }
  },
  hideEventTypeDetails: false,
  cssVarsPerTheme: {
    light: {
      "cal-border-booker": "#e5e7eb",
      "cal-text-emphasis": "#111827"
    },
    dark: {
      "cal-border-booker": "#374151",
      "cal-text-emphasis": "#f9fafb"
    }
  }
});

Configuration Options

The config object supports the following properties:

Prefill Fields

config: {
  name: "John Doe",
  email: "[email protected]",
  notes: "Meeting notes",
  guests: ["[email protected]", "[email protected]"],
  smsReminderNumber: "+1234567890"
}

UI Configuration

config: {
  theme: "dark",                         // "light" | "dark" | "auto"
  layout: "month_view",                  // "month_view" | "week_view" | "column_view"
  "ui.color-scheme": "dark",
  "ui.autoscroll": "false",             // Disable auto-scroll
  useSlotsViewOnSmallScreen: "true"     // Use slots view on mobile
}

Iframe Attributes

config: {
  iframeAttrs: {
    id: "my-cal-iframe",
    title: "Cal.com Booking"
  }
}

Event Handling

Listen to booking events to integrate with your application:

Listen to Events

Cal("init");

// Listen to booking success
Cal("on", {
  action: "bookingSuccessfulV2",
  callback: (e) => {
    const booking = e.detail.data;
    console.log("Booking created:", {
      title: booking.title,
      startTime: booking.startTime,
      endTime: booking.endTime,
      attendees: booking.attendees
    });
    
    // Track in analytics
    analytics.track("Booking Created", {
      eventType: booking.title,
      duration: booking.duration
    });
  }
});

Available Events

// Listen to all events
Cal("on", {
  action: "*",
  callback: (e) => {
    console.log("Event:", e.detail.type, e.detail.data);
  }
});

// Listen to specific events
Cal("on", {
  action: "bookerReady",
  callback: (e) => {
    console.log("Booker is ready");
  }
});

Cal("on", {
  action: "bookingCancelled",
  callback: (e) => {
    console.log("Booking cancelled:", e.detail.data);
  }
});

Remove Event Listeners

const callback = (e) => {
  console.log(e.detail.data);
};

// Add listener
Cal("on", {
  action: "bookingSuccessfulV2",
  callback: callback
});

// Remove listener
Cal("off", {
  action: "bookingSuccessfulV2",
  callback: callback
});

Using Namespaces

Namespaces allow multiple embeds on the same page without conflicts:
<div id="cal-1"></div>
<div id="cal-2"></div>

<script>
  // Initialize namespaces
  Cal("init", "namespace1");
  Cal("init", "namespace2");
  
  // Use namespaced embeds
  Cal.ns.namespace1("inline", {
    elementOrSelector: "#cal-1",
    calLink: "team/event-1"
  });
  
  Cal.ns.namespace2("inline", {
    elementOrSelector: "#cal-2",
    calLink: "team/event-2"
  });
  
  // Namespaced event listeners
  Cal.ns.namespace1("on", {
    action: "bookingSuccessfulV2",
    callback: (e) => console.log("Booking from namespace1")
  });
</script>

Performance Optimization

Prerendering

Preload the booking page for faster modal opening:
Cal("init");

// Prerender when user hovers over CTA
document.querySelector(".book-button").addEventListener("mouseenter", () => {
  Cal("prerender", {
    calLink: "organization/event-type",
    type: "modal",
    pageType: "team.event.booking.slots"
  });
});

Preloading with Router

For routing forms, prerender with routing parameters:
Cal("prerender", {
  calLink: "router?formId=123&department=sales",
  type: "modal",
  pageType: "team.event.booking.slots"
});

Advanced Examples

Dynamic Configuration

Update configuration based on user actions:
Cal("init");
Cal("inline", {
  elementOrSelector: "#my-cal",
  calLink: "organization/event-type"
});

// Update theme dynamically
document.querySelector("#dark-mode-toggle").addEventListener("click", () => {
  Cal("ui", {
    theme: "dark"
  });
});

Integration with Forms

Prefill booking data from a form:
<form id="contact-form">
  <input type="text" name="name" placeholder="Name">
  <input type="email" name="email" placeholder="Email">
  <button type="submit">Book Meeting</button>
</form>

<script>
  Cal("init");
  
  document.querySelector("#contact-form").addEventListener("submit", (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    
    Cal("modal", {
      calLink: "organization/event-type",
      config: {
        name: formData.get("name"),
        email: formData.get("email")
      }
    });
  });
</script>

Auto-forward Query Parameters

Forward URL query parameters to the embed:
<script>
  Cal.config = Cal.config || {};
  Cal.config.forwardQueryParams = true;
  
  Cal("init");
  Cal("inline", {
    elementOrSelector: "#my-cal",
    calLink: "organization/event-type"
  });
</script>
Now parameters like ?name=John&[email protected] will be forwarded to the embed.

Element Click Embed

Use data attributes for zero-JavaScript modal embeds:
<!-- Add multiple booking buttons -->
<button 
  data-cal-link="organization/event-type-1"
  data-cal-config='{"theme":"light"}'
>
  Book Sales Call
</button>

<button 
  data-cal-link="organization/event-type-2"
  data-cal-config='{"theme":"dark"}'
>
  Book Support Call
</button>

<!-- Initialize once -->
<script>
  Cal("init");
</script>

With Namespace

<button 
  data-cal-namespace="sales"
  data-cal-link="organization/sales-call"
>
  Book Sales Call
</button>

<script>
  Cal("init", "sales");
</script>

Debugging

Enable Logging

Add cal.embed.logging=1 to your page URL:
https://example.com/booking?cal.embed.logging=1
This will log:
  • Embed initialization
  • Parent-iframe communication
  • Event triggers
  • Configuration changes

Debug Mode

Enable debug mode during initialization:
Cal("init", {
  debug: true,
  uiDebug: true
});

Package Information

The embed snippet is part of the @calcom/embed-snippet package:
{
  "name": "@calcom/embed-snippet",
  "version": "1.3.3",
  "description": "Vanilla JS embed snippet that loads @calcom/embed-core"
}
The snippet:
  • Loads @calcom/embed-core dynamically
  • Queues commands until the core library is ready
  • Supports multiple namespaces
  • Has zero dependencies
  • Minimal file size (~2KB)

Best Practices

Call Cal("init") once, preferably near the closing </body> tag. Multiple initializations are safe but unnecessary.
Ensure your elementOrSelector targets exist in the DOM before calling inline embed methods.
Set up event listeners with Cal("on", ...) before triggering actions that might fire those events.
For single-page applications, remove event listeners when unmounting components to prevent memory leaks.

Next Steps

Customization Options

Learn about themes, styles, and branding

React Component

Use the React wrapper for React apps

Build docs developers (and LLMs) love