calcom/cal.com
Web
Active contributors: Romit, Eunjae, Pedro, Hariom, Benny
Purpose
apps/web is the main Cal.diy product — the Next.js 13+ application that serves the marketing pages, the public booking pages, the authenticated dashboard, the embed runtime entry, and the tRPC HTTP endpoint that the rest of the front end (and embeds) call. It is the only app most self-hosted instances actually deploy.
Directory layout
apps/web/
├── app/ # Next.js App Router routes
│ ├── (booking-page-wrapper)/ # Public booking flow group
│ ├── (use-page-wrapper)/ # Authenticated app group (settings, bookings, ...)
│ ├── api/ # App Router API handlers
│ ├── cache/ # Cache utilities
│ ├── e2e/ # E2E helper routes (only in test builds)
│ ├── error.tsx, global-error.tsx
│ ├── icons/, fonts/, public/
│ ├── layout.tsx, page.tsx
│ └── not-found.tsx, notFoundClient.tsx
├── pages/ # Pages Router (legacy API + a few pages)
│ └── api/ # 39 legacy API endpoints
├── components/ # Shared React components specific to the web app
├── modules/ # 35 feature modules (api-keys, bookings, event-types, ...)
├── lib/ # App-local utilities
├── server/ # Server-only helpers
├── playwright/ # E2E specs
├── public/ # Static assets, including i18n locales
├── scripts/ # Build/runtime helpers
├── proxy.ts # Reverse-proxy logic for routing across surfaces
├── proxy.test.ts # Tests for proxy.ts
├── instrumentation.ts # Sentry instrumentation entry
├── instrumentation-client.ts # Browser-side Sentry init
├── sentry.server.config.ts
├── sentry.edge.config.ts
├── next.config.ts # 19 KB next.config — feature flags, headers, rewrites
├── pagesAndRewritePaths.ts # Centralized rewrite/redirect rules
├── getNextjsOrgRewriteConfig.ts
├── trigger.version.ts
└── package.jsonApp Router groups
apps/web/app/(use-page-wrapper)/(main-nav)/ is the authenticated dashboard:
| Path | Purpose |
|---|---|
event-types/ |
Manage your bookable offerings |
bookings/ |
Past, upcoming, cancelled bookings |
availability/ |
Schedule editor |
members/ |
Team members (where applicable) |
booking/, apps/web/app/(booking-page-wrapper) |
Booking detail + public booking flow |
Other top-level App Router groups: auth, apps, signup, onboarding, settings, payment, video, enterprise (legacy, being removed), more, refer, upgrade, getting-started, maintenance.
Pages Router
apps/web/pages/api/ retains 39 legacy API routes. The most important is pages/api/trpc/[trpc].ts, which mounts every tRPC router. The rest are integration callbacks (OAuth redirects, webhook receivers from Stripe / Daily / etc.) that have not yet been ported to the App Router.
Modules
apps/web/modules/ contains 35 directories — one per UI feature. They are the page-level glue between packages/features/<domain>/ (business logic + components) and the App Router pages. For example:
apps/web/modules/event-types/— event-type editor screensapps/web/modules/bookings/— bookings list + detailapps/web/modules/availability/— schedule UIapps/web/modules/auth/— login, signup, magic-link formsapps/web/modules/onboarding/,getting-started/— first-time setup wizardapps/web/modules/embed/— embed-specific routes and SSRapps/web/modules/troubleshooter/— availability troubleshooter
The pattern is intentional: thin app/.../page.tsx files import a View component from apps/web/modules/<feature>/, which composes pieces from packages/features/<feature>/components/ and packages/ui/.
Key abstractions
| Symbol | File | Purpose |
|---|---|---|
WithEmbedSSR |
apps/web/app/WithEmbedSSR.tsx |
Wraps a page with embed-aware server rendering |
WithAppDirSsr |
apps/web/app/WithAppDirSsr.tsx |
Bridge for SSR helpers between Pages and App Router |
AppRouterI18nProvider |
apps/web/app/AppRouterI18nProvider.tsx |
Loads i18n bundles for App Router pages |
pagesAndRewritePaths |
apps/web/pagesAndRewritePaths.ts |
Single list of paths the proxy + rewrites need to know about |
proxy.ts |
apps/web/proxy.ts |
Routes incoming traffic across legacy and new surfaces |
next.config.ts |
apps/web/next.config.ts |
Feature flags, security headers, rewrites, image config |
trigger.version.ts |
apps/web/trigger.version.ts |
Pinned Trigger.dev SDK version |
How it works
graph TD
Visitor -->|HTTP| Proxy[apps/web/proxy.ts]
Proxy -->|App Router| AppRouter[app/]
Proxy -->|Pages Router| Pages[pages/]
AppRouter -->|tRPC HTTP| TRPCEndpoint[pages/api/trpc/[trpc].ts]
TRPCEndpoint -->|delegates| TRPCServer[packages/trpc routers]
AppRouter -->|loadable| Modules[apps/web/modules/*]
Modules --> Features[packages/features/*]
Pages -->|webhooks| Features
NextAuth[NextAuth callbacks] --> AuthFeature[packages/features/auth]A typical authenticated request flow:
- The browser hits an App Router route like
/event-types. - Next.js resolves the layout chain. Permission checks happen in
page.tsx(perAGENTS.md). - The page imports a
Viewfromapps/web/modules/event-types/. The View pulls data via tRPC client hooks generated frompackages/trpc. - tRPC calls land at
pages/api/trpc/[trpc].ts, which delegates topackages/trpc/server/createNextApiHandler.ts. - Routers under
packages/trpc/server/routers/invoke services inpackages/features/<domain>/serviceswhich call repositories which talk to Prisma.
Public booking flow uses the (booking-page-wrapper) group and similarly threads through packages/features/bookings.
Integration points
- NextAuth —
pages/api/auth/[...nextauth].tsmounts NextAuth usingpackages/features/auth/lib/next-auth-options.tsand the custom adapterpackages/features/auth/lib/next-auth-custom-adapter.ts. - tRPC —
pages/api/trpc/[trpc].tsis the single mount; all routers come frompackages/trpc/server/routers. - Stripe webhooks —
pages/api/integrations/stripepayment/webhook.tsplus the Stripe app underpackages/app-store/stripepayment. - Calendar webhooks —
pages/api/integrations/<provider>/webhookfor Google Calendar push notifications, Office 365, etc. - Embeds — the embed runtime mounts via
apps/web/modules/embed/and usespackages/embeds/embed-core. The published JS bundle lives atapps/web/public/embed/embed.js. - Sentry —
instrumentation.ts+sentry.*.config.ts. - Trigger.dev —
apps/web/trigger.version.tsplus jobs declared inpackages/features/trigger.config.ts.
Entry points for modification
- Adding a new authenticated page: create
apps/web/app/(use-page-wrapper)/(main-nav)/<route>/page.tsx, point it at a View inapps/web/modules/<route>/. Permission checks belong inpage.tsx, never inlayout.tsx(AGENTS.md). - Adding a new public route: prefer the
(booking-page-wrapper)group if it is booking-related. For unauthenticated marketing-style pages, create the route underapps/web/app/directly. - Adding a new API endpoint: prefer adding a tRPC procedure in
packages/trpc/server/routers/.... Only fall back to a Pages-Router handler underapps/web/pages/api/for webhooks or callbacks that need raw HTTP semantics. - Changing rewrites/headers: edit
apps/web/next.config.tsandapps/web/pagesAndRewritePaths.ts. The proxy logic inapps/web/proxy.tsstays in sync with these.
Related pages
- API v2 — the separate NestJS service
- Packages → trpc — how the routers mount inside this app
- Features → bookings — the largest business-logic module the web app depends on
- Features overview
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.