Register
Unlimited, free, no approval. Anonymous visitor.
Public event sidebar card — compose with CardFrame parts, fact rows, and StatusMark.
Compose the public Cal Event registration card from EventRegistrationSection parts. Branching (registration state, sign-in, which rows to show) lives in the feature layer — see resolveEventRegistrationPresentation and cal-events-registrations.
import {
EventRegistrationSection,
EventRegistrationSectionActions,
EventRegistrationSectionFact,
EventRegistrationSectionFacts,
EventRegistrationSectionHeader,
EventRegistrationSectionJoiningAs,
EventRegistrationSectionPanel,
EventRegistrationSectionStatus,
EventRegistrationSectionStatusDescription,
EventRegistrationSectionStatusTitle,
EventRegistrationSectionTitle,
} from "@calcom/features/cal-events/components/event-page/sections/event-registration-section-parts";
import { StatusMark } from "@coss/ui/shared/status-mark";Feature — not @coss/ui/shared. Compound parts live under packages/features/cal-events because registration copy, registration-state branching, and i18n wiring belong to Cal Events. Production renders them through EventRegistrationCard (event-registration-card.tsx), which composes the parts, the named facts and the resolver from the public page's data.
CardFrame header (py-3), optional fact rows, optional centered status (StatusMark + title + description), then a full-width action stack. Put identity under the action inside EventRegistrationSectionActions.
Joining as pro@example.com
<EventRegistrationSection>
<EventRegistrationSectionHeader>
<EventRegistrationSectionTitle>Registration</EventRegistrationSectionTitle>
</EventRegistrationSectionHeader>
<EventRegistrationSectionPanel>
<EventRegistrationSectionFacts>
<EventRegistrationSectionFact icon={ClockIcon} title="8 spots remaining" subtitle="..." />
</EventRegistrationSectionFacts>
<EventRegistrationSectionActions>
<Button size="lg">Register</Button>
<EventRegistrationSectionJoiningAs label="Joining as" email="pro@example.com" />
</EventRegistrationSectionActions>
</EventRegistrationSectionPanel>
</EventRegistrationSection>Spots remaining, approval required, price — each is an EventRegistrationSectionFact inside EventRegistrationSectionFacts. EventRegistrationSectionFact is the generic row: you pass the icon, title and subtitle. Below it are the specific facts made for each case; they pick the icon and copy for you, so use those and reach for the generic row only when there is no specific fact.
<EventRegistrationSectionFact
icon={UserPlusIcon}
title="Approval required"
subtitle="The host must approve your request before you're in"
/>EventRegistrationSectionFactPrice renders the price row. Pass it the event's cheapest price and whether the locations charge different amounts or include a free one; the public event page already has these on data. Price fact formats the amount and picks the wording. Free events render nothing.
| Prop | Meaning |
|---|---|
price | Cheapest price in the currency's smallest unit (cents for USD); 0 or null when the event is free |
currency | Currency code of that price, e.g. usd |
priceIsFrom | Locations charge different amounts, so the title reads "From $25.00" |
hasFreeOption | At least one location is free, so the title reads "Free – $25.00" |
// Public event page: `data` is the CalEventPublicDto that getCalEventPublicData builds on the server.
<EventRegistrationSectionFacts>
<EventRegistrationSectionFactPrice
price={data.price}
currency={data.currency}
priceIsFrom={data.priceIsFrom}
hasFreeOption={data.hasFreeOption}
/>
</EventRegistrationSectionFacts>| Locations | Title | Subtitle |
|---|---|---|
| Every location charges the same | $25.00 | Paid event |
| Different prices, none free | From $25.00 | Depends on your location choice |
| At least one location is free | Free – $25.00 | Depends on your location choice |
| No priced location | Renders nothing |
EventRegistrationSectionFactSpotsRemaining renders the seats-left row. Pass it the number of spots remaining. On the public event page getCalEventPublicData counts confirmed guests against the event's seat limit and puts the result on data.capacity.spotsRemaining. Spots remaining fact picks the one-spot or many-spots wording and adds the same subtitle under both. It renders nothing when the event is full or has no capacity limit.
| Prop | Meaning |
|---|---|
spotsRemaining | Seats left; null when the event has no capacity limit |
// Public event page: `data` is the CalEventPublicDto that getCalEventPublicData builds on the server.
<EventRegistrationSectionFacts>
<EventRegistrationSectionFactSpotsRemaining spotsRemaining={data.capacity.spotsRemaining} />
</EventRegistrationSectionFacts>| Spots remaining | Title | Subtitle |
|---|---|---|
| 2 or more | 8 spots remaining | Register before it fills up |
| 1 | One spot remaining | Register before it fills up |
| 0 | Renders nothing | |
| No capacity limit | Renders nothing |
EventRegistrationSectionFactApprovalRequired renders the row telling guests the host has to approve them. It takes no data; the public event page shows it when data.requiresApproval is true.
// Public event page: `data` is the CalEventPublicDto that getCalEventPublicData builds on the server.
<EventRegistrationSectionFacts>
{data.requiresApproval ? <EventRegistrationSectionFactApprovalRequired /> : null}
</EventRegistrationSectionFacts>| Title | Subtitle |
|---|---|
| Approval required | The host must approve your request before you're in |
<EventRegistrationSectionStatus>
<StatusMark status="success" className="mx-auto" />
<EventRegistrationSectionStatusTitle>You're in!</EventRegistrationSectionStatusTitle>
<EventRegistrationSectionStatusDescription>
Your spot is confirmed. Check your email for event details and updates
</EventRegistrationSectionStatusDescription>
</EventRegistrationSectionStatus>Every state the card is meant to render. Cancelled events render nothing — the page already owns that state.
Register
Unlimited, free, no approval. Anonymous visitor.
Register · signed in (form)
Same CTA as anonymous. Footer is email only — no name, no avatar. Used when one-click cannot run (questions, public names, multi-location, missing profile name).
Joining as pro@example.com
Register in one click
Signed-in, complete identity, no extra questions, single location, not approval, guest names off.
Joining as pro@example.com
One-click submitting
Joining as pro@example.com
Spots remaining
One spot remaining
Approval required
Approval + spots
Fixed price
From $X (locations differ)
Free – $X (one location free)
Price + spots
Price + approval
Price + spots + approval
From $X + spots
Free – $X + approval
One-click + price
One-click allows a single priced location. From/Free–$X need multiple locations, so they cannot one-click.
Joining as pro@example.com
One-click + spots
Joining as pro@example.com
One-click + price + spots
Joining as pro@example.com
Request to join
Approval blocks one-click. Footer is still Joining as email.
Joining as pro@example.com
Request to join + spots
Joining as pro@example.com
Join waitlist
Joining as pro@example.com
Sold out
Registration closed
Closed copy, not a button. No footer: the viewer can no longer join.
Sold out
Full, waitlist off. Spots row is omitted when remaining is 0.
Join waitlist
Full, waitlist on.
Sold out + approval
Approval wins: still Request to join.
Registration closed
Event has started. CTA is copy, not a button.
Cancelled
Today still paints a cancelled CTA. The page already has a cancelled Alert.
Registration closed (ended)
Event has ended. CTA is copy, not a button.
You're in
Footer stays: Joining as email, not name/avatar.
You're in + spots
Spots row is not suppressed after you join.
You're in + price
You're in + approval row
requiresApproval is still true on the event after you are confirmed.
Pending approval
Pending + spots
On the waitlist
Pay now
Complete payment to confirm your spot
Joining as ana@example.com
Pay now + spots
Complete payment to confirm your spot
Joining as ana@example.com
Payment unavailable
You're in
Restored from the RSVP cookie: no footer, but the ticket and cancel actions are theirs.
You're in + price
Pending approval
On the waitlist
Pay now
Complete payment to confirm your spot
Payment unavailable
You're hosting
Footer hidden. No sales rows.
You're hosting this event
Hosting + spots
You're hosting this event
Hosting a paid event
You're hosting this event
Hosting + approval
You're hosting this event
Hosting + price + spots + approval
You're hosting this event
Host on a cancelled event
Today still paints a cancelled CTA. Then drops the card, same as a guest.
| Part | data-slot | Notes |
|---|---|---|
EventRegistrationSection | event-registration-section | Root CardFrame. |
EventRegistrationSectionHeader | event-registration-section-header | Default py-3. |
EventRegistrationSectionTitle | event-registration-section-title | Do not restyle. |
EventRegistrationSectionPanel | event-registration-section-panel | Card + CardPanel. |
EventRegistrationSectionFacts | event-registration-section-facts | Row stack; hidden when empty. |
EventRegistrationSectionFact | event-registration-section-fact | Icon tile + title + subtitle. |
EventRegistrationSectionFactPrice | event-registration-section-fact | Price row; data-testid="event-registration-fact-price". |
EventRegistrationSectionFactSpotsRemaining | event-registration-section-fact | Seats-left row; data-testid="event-registration-fact-spots". |
EventRegistrationSectionFactApprovalRequired | event-registration-section-fact | Approval row; data-testid="event-registration-fact-approval". |
EventRegistrationSectionStatus | event-registration-section-status | Centered status block. |
EventRegistrationSectionStatusTitle | event-registration-section-status-title | Uses EmptyTitle typography. |
EventRegistrationSectionStatusDescription | event-registration-section-status-description | Uses EmptyDescription typography. |
EventRegistrationSectionActions | event-registration-section-actions | Full-width CTA stack. |
EventRegistrationSectionActionIntro | event-registration-section-action-intro | Line above the action (Pay now). |
EventRegistrationSectionActionHint | event-registration-section-action-hint | Muted line under the action (host). |
EventRegistrationSectionIdentity | event-registration-section-identity | Generic identity slot under the action. |
EventRegistrationSectionJoiningAs | event-registration-section-identity | Signed-in identity line; pass a translated label and email. |
The generic parts do not call the i18n layer; the named facts do.
| Part | Who translates |
|---|---|
EventRegistrationSectionTitle, status title/description, action intro/hint, buttons | Caller passes translated children or button labels |
EventRegistrationSectionFact | Caller passes translated title and subtitle |
EventRegistrationSectionFactPrice | The fact, with cal_events_paid_event, cal_events_price_from, cal_events_price_free_to, cal_events_price_depends_on_location |
EventRegistrationSectionFactSpotsRemaining | The fact, with cal_events_spots_remaining, cal_events_one_spot_remaining |
EventRegistrationSectionFactApprovalRequired | The fact, with cal_events_approval_required, cal_events_approval_required_description |
EventRegistrationSectionJoiningAs | Caller passes translated label; email is data |
Production wiring lives in apps/web (or a feature component using useLocale), matching event-public-view.tsx. Key: cal_events_joining_as in common.json.
| File | Notes |
|---|---|
packages/features/cal-events/components/event-page/sections/event-registration-card.tsx | EventRegistrationCard, the production composition used by the public event page and the editor preview |
apps/design-system/components/registration-frame-playground-data.tsx | renderEventRegistrationSection for design-system fixtures |
apps/design-system/components/event-registration-section-examples.tsx | Docs examples |
| Path | Notes |
|---|---|
/docs/composites/event-registration-section | Docs and all registration states |
packages/features/cal-events/components/event-page/sections/event-registration-section-parts.tsxevent-registration-section-fact-price.tsx, event-registration-section-fact-spots-remaining.tsx, event-registration-section-fact-approval-required.tsxpackages/features/cal-events/components/event-page/sections/resolve-event-registration-presentation.tspackages/features/cal-events/components/event-page/sections/event-registration-card.tsxOn This Page