Skip to main content
The @calcom/embed-react package provides a React component and hooks for seamlessly integrating Cal.com into your React applications with full TypeScript support.

Installation

npm install @calcom/embed-react

Peer Dependencies

The package requires React 18+ or React 19+:
{
  "peerDependencies": {
    "react": "^18.2.0 || ^19.0.0",
    "react-dom": "^18.2.0 || ^19.0.0"
  }
}

Quick Start

Inline Embed Component

The simplest way to embed Cal.com is using the Cal component:
import Cal from "@calcom/embed-react";

function BookingPage() {
  return (
    <Cal
      calLink="organization/event-type"
      style={{ width: "100%", height: "100%", overflow: "scroll" }}
    />
  );
}

With Configuration

Prefill booking information and customize the appearance:
import Cal from "@calcom/embed-react";

function BookingPage() {
  return (
    <Cal
      calLink="pro"
      config={{
        name: "John Doe",
        email: "[email protected]",
        notes: "Test Meeting",
        guests: ["[email protected]"],
        theme: "dark",
        layout: "month_view"
      }}
      style={{ width: "100%", height: "100%", overflow: "scroll" }}
    />
  );
}

Component Props

The Cal component accepts the following props:

CalProps Interface

type CalProps = {
  calLink: string;                           // Required: Cal.com booking link
  calOrigin?: string;                        // Cal.com origin URL (default: https://cal.com)
  namespace?: string;                        // Namespace for multiple embeds
  config?: PrefillAndIframeAttrsConfig;     // Configuration and prefill data
  initConfig?: {
    debug?: boolean;                         // Enable debug logging
    uiDebug?: boolean;                       // Enable UI debug mode
  };
  embedJsUrl?: string;                       // Custom embed.js URL
} & React.HTMLAttributes<HTMLDivElement>;   // Accepts all div attributes

Configuration Options (config prop)

type PrefillAndIframeAttrsConfig = {
  // Prefill fields
  name?: string;
  email?: string;
  notes?: string;
  guests?: string[];
  
  // UI Configuration
  theme?: "light" | "dark" | "auto";
  layout?: "month_view" | "week_view" | "column_view";
  "ui.color-scheme"?: string;
  "ui.autoscroll"?: "true" | "false";
  
  // Feature flags
  "flag.coep"?: "true" | "false";
  useSlotsViewOnSmallScreen?: "true" | "false";
  
  // Iframe attributes
  iframeAttrs?: {
    id?: string;
    [key: string]: string;
  };
  
  // Any additional query params
  [key: string]: string | string[] | Record<string, string>;
};

Using getCalApi

For more advanced use cases, use the getCalApi function to interact with the Cal API programmatically:

Basic Usage

import { useEffect } from "react";
import { getCalApi } from "@calcom/embed-react";

function App() {
  useEffect(() => {
    (async function () {
      const cal = await getCalApi();
      
      cal("inline", {
        elementOrSelector: "#my-cal-inline",
        calLink: "organization/event-type"
      });
    })();
  }, []);
  
  return <div id="my-cal-inline" />;
}

With Namespace

import { useEffect } from "react";
import { getCalApi } from "@calcom/embed-react";

function App() {
  useEffect(() => {
    (async function () {
      const cal = await getCalApi({ namespace: "booking" });
      
      cal("inline", {
        elementOrSelector: "#my-cal",
        calLink: "organization/event-type"
      });
    })();
  }, []);
  
  return <div id="my-cal" />;
}

Event Handling

Listen to booking events using the event system:

Type-Safe Event Handling

import { useEffect } from "react";
import { getCalApi, type EmbedEvent } from "@calcom/embed-react";

function App() {
  useEffect(() => {
    (async function () {
      const cal = await getCalApi({ namespace: "inline" });
      
      // Listen to booking success events
      const bookingSuccessCallback = (e: EmbedEvent<"bookingSuccessfulV2">) => {
        const data = e.detail.data;
        console.log("Booking created:", {
          title: data.title,
          startTime: data.startTime,
          endTime: data.endTime
        });
      };
      
      cal("on", {
        action: "bookingSuccessfulV2",
        callback: bookingSuccessCallback
      });
      
      // Cleanup
      return () => {
        cal("off", {
          action: "bookingSuccessfulV2",
          callback: bookingSuccessCallback
        });
      };
    })();
  }, []);
  
  return <div id="my-cal" />;
}

Available Events

// Listen to all events
cal("on", {
  action: "*",
  callback: (event) => {
    console.log(event.detail);
  }
});

// Specific events
cal("on", {
  action: "bookingSuccessfulV2",
  callback: (e: EmbedEvent<"bookingSuccessfulV2">) => {
    // Handle successful booking
  }
});

cal("on", {
  action: "bookerReady",
  callback: (e: EmbedEvent<"bookerReady">) => {
    // Handle when booker is ready
  }
});

Customizing UI

Customize the embed appearance dynamically:
import { useEffect } from "react";
import { getCalApi } from "@calcom/embed-react";

function App() {
  useEffect(() => {
    (async function () {
      const cal = await getCalApi({ namespace: "inline" });
      
      // Apply custom styles
      cal("ui", {
        theme: "dark",
        styles: {
          branding: {
            brandColor: "#000000"
          }
        },
        hideEventTypeDetails: false,
        cssVarsPerTheme: {
          light: {
            "cal-border-booker": "red",
            "cal-border-booker-width": "20px"
          },
          dark: {
            "cal-border-booker": "blue",
            "cal-border-booker-width": "5px"
          }
        }
      });
    })();
  }, []);
  
  return <div id="my-cal" />;
}
Create a modal embed triggered by a button:
import { useEffect } from "react";
import { getCalApi } from "@calcom/embed-react";

function App() {
  useEffect(() => {
    (async function () {
      const cal = await getCalApi({ namespace: "modal" });
      
      cal("ui", {
        styles: { 
          branding: { 
            brandColor: "#000000" 
          } 
        },
        hideEventTypeDetails: false
      });
    })();
  }, []);
  
  return (
    <button
      data-cal-namespace="modal"
      data-cal-link="organization/event-type"
      data-cal-config='{"layout":"month_view", "theme":"dark"}'
    >
      Book a meeting
    </button>
  );
}

Floating Button

Add a persistent floating button:
import { useEffect } from "react";
import { getCalApi } from "@calcom/embed-react";

function App() {
  useEffect(() => {
    (async function () {
      const cal = await getCalApi({ namespace: "floating" });
      
      cal("floatingButton", {
        calLink: "organization/event-type",
        config: {
          theme: "dark"
        }
      });
      
      cal("ui", { 
        styles: { 
          branding: { 
            brandColor: "#000000" 
          } 
        }, 
        hideEventTypeDetails: false 
      });
    })();
  }, []);
  
  return null;
}

Multiple Embeds with Namespaces

Use namespaces to have multiple embeds on the same page:
import Cal from "@calcom/embed-react";

function BookingPage() {
  return (
    <>
      <Cal
        namespace="embed1"
        calLink="organization/event-type-1"
        config={{ theme: "light" }}
        style={{ width: "100%", height: "600px" }}
      />
      
      <Cal
        namespace="embed2"
        calLink="organization/event-type-2"
        config={{ theme: "dark" }}
        style={{ width: "100%", height: "600px" }}
      />
    </>
  );
}

TypeScript Support

The package includes full TypeScript definitions:
import type { EmbedEvent, PrefillAndIframeAttrsConfig } from "@calcom/embed-react";

// Type-safe event handling
const handleBooking = (e: EmbedEvent<"bookingSuccessfulV2">) => {
  const { title, startTime, endTime } = e.detail.data;
  // TypeScript knows the exact shape of the data
};

// Type-safe configuration
const config: PrefillAndIframeAttrsConfig = {
  name: "John Doe",
  email: "[email protected]",
  theme: "dark",
  layout: "month_view"
};

Custom Embed URL

For self-hosted instances, specify a custom embed URL:
import Cal from "@calcom/embed-react";

function BookingPage() {
  return (
    <Cal
      calLink="organization/event-type"
      calOrigin="https://cal.example.com"
      embedJsUrl="https://cal.example.com/embed/embed.js"
    />
  );
}

Best Practices

When you have multiple embeds on the same page, always use unique namespaces to prevent conflicts.
Always remove event listeners in the cleanup function to prevent memory leaks.
The getCalApi function is asynchronous. Handle loading states appropriately in your UI.
Ensure the container has appropriate dimensions. The embed will fill its container.

Next Steps

Customization

Learn about theme and styling options

JavaScript Snippet

Alternative implementation for non-React apps

Build docs developers (and LLMs) love