Embeds Overview

Purpose and Scope

The embeds workspace is the part of Cal.diy that lets a booking link appear inside another website instead of requiring a visitor to navigate directly to the scheduling application. In practical terms, it gives site owners a way to open Cal booking flows as inline content, modal experiences, floating-button flows, or framework components while still relying on the same booking pages served by the Cal.diy web application. The top-level embeds README defines this folder as the home for the different embed flavors, and the individual package READMEs explain how each flavor is developed, tested, and shipped.

Sources: packages/embeds/README.md, packages/embeds/embed-core/README.md, packages/embeds/embed-snippet/README.md, packages/embeds/embed-react/README.md

This page is an orientation page rather than an installation tutorial. Use it to understand which embed package you are touching, what responsibilities belong to the shared runtime, and how the snippet and React package relate to that runtime. That distinction matters because many embed bugs look like integration problems from the outside, but the fix may belong in the vanilla core runtime, the loader snippet, or the React wrapper. The Cal.diy repository also carries a self-hosting caution in its user-facing docs, so embedded booking should be treated as part of a self-hosted deployment that the operator is responsible for running, securing, and upgrading.

Relevant Source Files

  • packages/embeds/README.md — Defines the embeds folder, distinguishes core and snippet, documents publishing, skeleton-loader support, prerendering, preloading, modal reuse, and the high-level core architecture.
  • packages/embeds/embed-core/README.md — Documents the vanilla JavaScript core script, development server, tests, production build artifact, page compatibility rules, and known improvement areas.
  • packages/embeds/embed-snippet/README.md — Documents the vanilla snippet package that fetches the core library and can be installed either as a module script or copied inline into a page.
  • packages/embeds/embed-react/README.md — Documents the React component package, its development command, test command, and wrapper-specific TODOs around Playwright, distribution, and TypeScript developer experience.

Package Roles

There are three main package roles to keep separate. The core package is the runtime: it is written in vanilla JavaScript and is responsible for embedding a Cal Link, managing iframe behavior, and providing the core API used by other entrypoints. The snippet package is a delivery mechanism: it is a vanilla JavaScript snippet that fetches the core library so a website can install the embed without adopting a framework package. The React package is a framework-facing wrapper: it lets React applications render a Cal Link as a component while still depending on the behavior implemented by the core runtime.

Sources: packages/embeds/README.md, packages/embeds/embed-core/README.md, packages/embeds/embed-snippet/README.md, packages/embeds/embed-react/README.md

A useful mental model is to treat the core package as the source of truth for browser-side embed behavior and the other packages as ergonomics layers. If an issue concerns iframe sizing, modal lifecycle, skeleton loaders, parent-to-iframe communication, or prerendering, start by reading the core documentation and core implementation. If an issue concerns how a customer copies code into a plain website, inspect the snippet. If an issue concerns React component props, React build output, or whether the wrapper calls the core API appropriately, inspect the React package. This separation keeps public integration surfaces small while allowing the shared runtime to evolve in one place.

Core Runtime Architecture

The top-level embeds README describes the core architecture as a multi-step bootstrap process. The embed script loads on the parent page, creates a global Cal entry point, initializes custom elements such as modal boxes, floating buttons, and inline embeds, and sets up a namespace-based action manager for event handling. The same README also calls out a message-based parent-iframe communication system, with parent commands for user interface configuration and connection-style configuration. That design lets the page hosting the embed remain outside the iframe while still controlling important embed behavior.

Sources: packages/embeds/README.md

The namespace concept is important for pages that host more than one booking experience. A parent page may need separate actions, modal state, or prerendered booking links for different Cal namespaces. The README’s routing-prerendering description shows this in action: when a call to action is clicked, the runtime checks whether a prerendered or currently prerendering modal exists for the namespace. If it does, the modal opens with a skeleton loader, the router endpoint is called, and query parameters are passed to the iframe so the iframe can update its URL with browser history replacement instead of a full reload.

The runtime also distinguishes preloading from prerendering. Preloading uses an iframe primarily to warm static assets in the browser cache before a user opens the booking experience. Prerendering continues from that preloaded iframe so the user actually books on the already prepared iframe. The README explicitly notes that prerendering is more complex but provides greater performance benefits, and that the public prerender API delegates to the preload API, which then decides whether the work is a preload or a prerender. This means performance work must preserve both behavior and public API expectations.

Loader, Page Compatibility, and User Experience

The skeleton loader is documented as supported only for certain page types, with a fallback to the default non-skeleton loader for other cases. The current support matrix includes responsive, mobile, and month-view layout support, while week-view and column-view layout support are still unchecked. Dark and light themes are supported, system theme changes should reflect without a page refresh, and some booking-form page types are only partially supported because they show a slots-page skeleton. This is a practical compatibility boundary: do not assume every booking route has a complete skeleton experience.

Sources: packages/embeds/README.md, packages/embeds/embed-core/README.md

Page authors also need to make booking pages compatible with the embed runtime. The core README says a page should define a main class on the element containing the entire page content and avoid auto margins there. That class allows iframe height adjustment so embedded content remains visible without unnecessary scrolling when device dimensions permit it. The same area also becomes part of modal behavior because clicks beyond it can close the modal box. When adding or modifying booking pages, this rule is a source-backed checklist item, not just a styling preference.

The known-bugs section in the core README is valuable because it identifies design constraints that are easy to miss during feature work. Unsupported browsers still need documentation and graceful handling. A booking shell is desired so common embed changes can be applied consistently. Accessibility, loader selection, back-button behavior, theme color configuration, transparent support for team links, inline border-radius behavior, branding placement, loader color customization, debug logging, error tracking, color-scheme simplification, and redirect-banner handling are all called out as unresolved or future concerns. These notes should shape acceptance criteria for embed changes.

Development, Testing, and Publishing Flow

For local core development, the core README instructs developers to run the development server and test the embed at the embed demo page. It also mentions a routing playground for prerendering with a headless router demo. Tests require the main application to be running on the expected application port and the embed-core development server to be running on the embed port before running the quick embed test command. The README also warns that the test helpers for getting the embed iframe and adding listeners work together but only support opening an embed on a fresh load.

Sources: packages/embeds/embed-core/README.md, packages/embeds/embed-react/README.md, packages/embeds/embed-snippet/README.md

The snippet and React packages have their own development notes because they sit at different integration points. The snippet README says building generates a module distribution file and calls out that React embed testing should use an up-to-date built snippet. It can be consumed as a module script or copied into a plain script tag. The React README describes a hot-reloading development command and tells developers working with website embeds to build after every change. Its testing command updates snapshots, and its TODO list suggests React-specific tests may only need to verify that the core API is invoked correctly.

Publishing is coordinated from the top-level embeds README. The documented process starts with creating a changeset, selecting embed packages only, versioning through changesets, getting the pull request reviewed and merged, and then publishing the embed packages with the repository’s custom embed publishing command. The README explains that the standard changesets publish command is not used because of workspace prefix removal limitations. This means release work is not just a package-manager action; it is a controlled workflow that should keep versions, changelogs, and package boundaries aligned.

Compact Reference

AreaSource-backed behaviorWhere to start
Core runtimeVanilla JavaScript script that embeds a Cal Link, manages custom elements, iframe behavior, bootstrap, and production build output.packages/embeds/embed-core/README.md
SnippetVanilla JavaScript loader that fetches the core package and can be installed as a module script or copied inline.packages/embeds/embed-snippet/README.md
ReactReact component surface for embedding a Cal Link while relying on the shared runtime behavior.packages/embeds/embed-react/README.md
PerformancePreload warms assets; prerender continues from a prepared iframe and delegates through the preload API.packages/embeds/README.md
Loader supportSkeleton loader is supported for selected booking slots and partially for booking forms, with other routes falling back.packages/embeds/README.md

Common developer commands documented by the package READMEs include running development servers, building package output, and executing quick embed tests. The core package documents the production build artifact as the script that should be made servable as the embed script URL. The React package documents a hot-reloading server and a snapshot-updating quick test command. The snippet package documents the build output used for module-script consumption. When changing shared runtime behavior, validate the core first, then rebuild and recheck wrapper packages that consume it.

Next Steps

If you are implementing or debugging embed behavior, begin with the package role that owns the failing layer. For iframe lifecycle, skeleton loader, routing prerendering, page compatibility, or parent-iframe messaging, read the core package next. For installation code that customers paste into non-framework sites, read the snippet package. For application teams using React, read the React package and confirm whether the issue is wrapper-specific or inherited from the shared runtime. For release work, follow the top-level embeds publishing process so changesets, package versions, and the custom embed publish command stay synchronized.