calcom/cal.com
Embeds
Purpose
Cal.diy's embed product lets external sites place a Cal scheduling experience anywhere — inline on a marketing page, as a modal triggered by a button, or as a floating button. The runtime is the published embed.js bundle from packages/embeds/embed-core; the React wrapper is @calcom/embed-react.
This is a feature view. For the package architecture, see packages/embeds.
Pieces
| Piece | Where | Audience |
|---|---|---|
embed.js runtime |
packages/embeds/embed-core |
Any website |
| React wrapper | packages/embeds/embed-react |
React sites |
| Snippet | packages/embeds/embed-snippet |
Generates the cal() global |
| Embed-side SSR | apps/web/app/WithEmbedSSR.tsx, apps/web/modules/embed/ |
The Cal app, when loaded inside an iframe |
| Embed-aware booking page | packages/features/bookings/Booker/ |
When ?embed=... query is present |
Embed handshake
The published bundle communicates with the embedded Cal page via postMessage. The full protocol diagrams are checked into packages/embeds/:
embed-handshake.mermaidembed-message-protocol.mermaidinline-embed-lifecycle.mermaidmodal-embed-lifecycle.mermaidmodal-prerendering-flow.mermaid
These are referenced from each package's README.
How an embed loads
sequenceDiagram
participant Host as Host site
participant Snippet
participant Core as embed-core
participant Iframe as Cal page (apps/web)
participant SSR as WithEmbedSSR
participant Booker as features/bookings/Booker
Host->>Snippet: <script src="/wikis/cal-com/features/embed.js">
Snippet->>Host: window.cal = ...
Host->>Core: cal("init", "user/event")
Core->>Iframe: insert iframe + handshake
Iframe->>SSR: identifies as embed
SSR->>Booker: render embed mode
Booker-->>Iframe: slot picker
Iframe-->>Core: postMessage("loaded")
Core-->>Host: emit "loaded" eventEmbed-side SSR
The Next.js app needs to render slightly differently when loaded inside an embed iframe:
- No app shell or navigation (the host site provides those).
- Custom theming based on host-passed CSS variables.
- Shorter / simpler layout components.
apps/web/app/WithEmbedSSR.tsx and apps/web/modules/embed/ handle this conditional rendering. Routes opt in by wrapping their layout/page with WithEmbedSSR.
Pre-rendering
Modal-mode embeds support pre-rendering: embed-core creates a hidden iframe at page load and only reveals it when the user clicks. This is documented in modal-prerendering-flow.mermaid.
Atoms vs embeds
packages/platform/atoms (the React SDK) and packages/embeds/embed-react overlap conceptually. The difference:
- Embeds are iframe-based and work for any host — minimal coupling, you embed the Cal site itself.
- Atoms call the v2 REST API directly and render their own UI. Tighter SDK integration but requires a Platform OAuth client.
Both are maintained, with new SDK consumers usually starting with atoms.
Integration points
- Web app —
apps/web/app/WithEmbedSSR.tsx,apps/web/modules/embed/recognize the embed mode at the route level. - Booking pipeline — embed-mode bookings post the same data, but the success redirect is replaced with a postMessage event.
- Cron / scripts — none. Embeds are a pure runtime.
Entry points for modification
- New embed mode: see packages/embeds.
- New embed-aware route: wrap with
WithEmbedSSR, add the layout exception inapps/web/modules/embed/, and verify with the embed Playwright suite (yarn e2e:embed).
Related pages
- packages/embeds
- packages/platform — the alternative SDK
- Bookings
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.