Email Templates Package

Purpose and Scope

The Email Templates Package is the shared email-rendering surface for Cal.diy. It contains JSX templates for the different messages the product sends and reusable building blocks that keep those messages visually and structurally consistent. The package README defines the core split clearly: components hold reusable patterns, while templates represent concrete types of email sent by the application. That distinction matters for contributors because most product work starts by selecting or adding a template, then composing it from the shared email components rather than duplicating markup for every notification. Sources: packages/emails/README.md, packages/emails/src/templates/index.ts, packages/emails/src/components/index.ts

The package is published inside the monorepo as the private workspace package named @calcom/emails. Its package manifest marks side effects as false, declares React and React DOM as peer dependencies for both React 18 and React 19 ranges, and depends on shared Cal.diy workspaces such as dayjs and lib. That tells maintainers that email templates are React-rendered artifacts but should stay reusable across the monorepo rather than depending on a single application runtime. The package also includes a local dx script that starts Docker Compose, matching the broader self-hosted development workflow. Sources: packages/emails/package.json

Relevant Source Files

  • packages/emails/README.md — Introduces the package model, shows the renderEmail usage pattern, and describes the local preview workflow.
  • packages/emails/package.json — Defines the @calcom/emails workspace package, dependency boundaries, peer React versions, and package scripts.
  • packages/emails/src/renderEmail.ts — Implements the public rendering helper that turns a named JSX template and props into static HTML.
  • packages/emails/src/templates/index.ts — Exports the concrete email templates that can be addressed by name through renderEmail.
  • packages/emails/src/components/index.ts — Exports reusable email layout and content primitives used by templates.

Core Primitives

The primary primitive is the template name. A template is an exported React component from the templates index, and the renderer accepts only names that exist in that namespace. Examples include attendee booking notifications, organizer notifications, account verification, OAuth client notifications, team invites, Daily recording and transcript messages, and app status notifications. Because the render helper is typed against the template export object, callers get a direct connection between the selected template and the props expected by that component. In practice, adding or renaming a template changes the public names available to the renderer. Sources: packages/emails/src/renderEmail.ts, packages/emails/src/templates/index.ts

The second primitive is the props object passed to the selected template. The README example renders TeamInviteEmail with a language helper, sender and recipient addresses, a team name, and a join link. That example demonstrates the normal usage contract: the first argument chooses the exported template, and the second argument supplies the template-specific data needed to construct the message. The package does not present a separate JSON schema for email inputs in the provided sources; instead, the React component prop type for each template is the source of truth used by TypeScript. Sources: packages/emails/README.md, packages/emails/src/renderEmail.ts

The third primitive is the reusable component layer. The component index exports base HTML wrappers, call-to-action helpers, booking confirmation content, information blocks, location and time details, participant detail blocks, separators, app status UI, manage links, and RawHtml. Those names show the intended composition style: templates should be assembled from established email-friendly blocks that already encode common Cal.diy patterns. That keeps transactional emails easier to review and makes visual or structural changes less scattered, especially when many booking lifecycle templates need similar sections for who, when, where, and what action to take. Sources: packages/emails/src/components/index.ts

Rendering API

The public rendering helper is the default export from renderEmail. Its implementation imports the template namespace, accepts a generic template key, derives the corresponding React component props, dynamically imports react-dom/server, and returns static markup for the rendered component. The dynamic server import keeps the implementation focused on server-side rendering at the moment an email body is needed. The TypeScript generic shape is important because it binds the template argument to the exact component selected from the templates index, reducing the risk of passing unrelated props to a different notification type. Sources: packages/emails/src/renderEmail.ts

import renderEmail from "@calcom/emails/renderEmail";
 
await renderEmail("TeamInviteEmail", {
  language: t,
  from: "teampro@example.com",
  to: "pro@example.com",
  teamName: "Team Pro",
  joinLink: "https://cal.com",
});

After rendering the JSX component to static markup, the helper performs two HTML post-processing steps. First, it removes empty script tags created by the RawHtml pathway, preventing a harmless implementation artifact from being emitted into final email HTML. Second, it replaces the opening html tag with one that includes XHTML, VML, and Microsoft Office namespace declarations. That replacement is email-client-specific infrastructure: transactional email HTML often needs compatibility markup for clients with legacy rendering behavior, and this package centralizes that detail so individual templates do not need to repeat it. Sources: packages/emails/src/renderEmail.ts

Template and Component Catalog

The template catalog is broad enough to cover booking lifecycle events, account and organization administration, app failures, OAuth client review outcomes, reminders, payments, recording and transcript delivery, and verification flows. Attendee and organizer variants are both present, which reflects a common scheduling product requirement: the same booking event may need different wording and links depending on whether the recipient is the person booking or the person hosting. Contributors should therefore avoid assuming that a single template covers both sides of a workflow when the templates index already exposes recipient-specific versions. Sources: packages/emails/src/templates/index.ts

Public surfaceExamples visible in the indexPurpose
Booking attendee templatesAttendeeScheduledEmail, AttendeeRescheduledEmail, AttendeeCancelledEmailNotify invitees about booking state changes.
Booking organizer templatesOrganizerScheduledEmail, OrganizerRequestEmail, OrganizerCancelledEmailNotify hosts about bookings, requests, and cancellations.
Account and verification templatesForgotPasswordEmail, VerifyAccountEmail, VerifyEmailByCodeSupport identity and account confirmation flows.
Admin and OAuth templatesAdminOAuthClientNotificationEmail, OAuthClientApprovedNotificationEmailNotify administrators and developers about OAuth-related decisions.
App and integration templatesBrokenIntegrationEmail, DisabledAppEmail, AppsStatusCommunicate app health or integration status.

Component exports provide a smaller vocabulary for building those templates consistently. BaseEmailHtml and V2BaseEmailHtml suggest versioned or variant base layouts, while CallToAction, CallToActionTable, and CallToActionIcon centralize action presentation. WhoInfo, WhenInfo, LocationInfo, UserFieldsResponses, and BookingConfirmationForm map directly to booking email content. RawHtml exists for situations where a template needs to include prebuilt HTML, but renderEmail also accounts for RawHtml side effects by stripping injected empty scripts from the output. That pairing is a useful guardrail for contributors working near low-level markup. Sources: packages/emails/src/components/index.ts, packages/emails/src/renderEmail.ts

Development and Preview Workflow

The package README recommends previewing email HTML through an API endpoint, specifically noting an existing endpoint at /apps/web/pages/api/email.ts that can be changed to the template currently being worked on. The practical workflow is to select a template from the templates index, pass realistic props, render it through the preview endpoint, and inspect the resulting HTML in a browser or email testing path. This is intentionally close to the final render path, because the same renderEmail helper is responsible for converting JSX into static markup suitable for outbound email delivery. Sources: packages/emails/README.md, packages/emails/src/renderEmail.ts

When adding a new email, start by deciding whether the message is truly a new notification type or a variation of an existing one. If it is new, create the React template, export it from the templates index, and use the shared component exports for layout, call-to-action, participant, timing, and location sections where possible. Then render it by its exported name through the preview endpoint and verify that the static HTML contains the expected content and email namespace handling. If the self-hosted instance sends workflow-related email, remember that the first-party SendGrid setup requires SENDGRID_API_KEY and SENDGRID_EMAIL to be configured for outbound delivery.

Compact Reference

ItemSource-backed contract
Package name@calcom/emails
Renderer import@calcom/emails/renderEmail
Renderer inputsTemplate export name plus props for that template component
Renderer outputStatic email HTML string with script cleanup and email namespace replacement
Template registrypackages/emails/src/templates/index.ts
Component registrypackages/emails/src/components/index.ts
Local preview guidanceUse the existing web API email preview endpoint and switch it to the template under development

Next Steps

For most contributors, the safest path is to treat templates as the public email catalog and components as the shared design system for email markup. Before changing an existing message, check whether both attendee and organizer variants need updates. Before adding a new message, confirm its exported name is clear, include it in the templates index, and preview the result through the web email endpoint with realistic props. Readers working on outbound delivery rather than markup should pair this page with the SendGrid integration guidance, because rendering HTML and configuring the mail provider are separate parts of the self-hosted email path.