calendar-tsx

Server rendering

Fetch on the server and keep the current time identical on the server and the client.

Hydration

Today's highlight and the now indicator depend on the current time. If the server and the client use different times, for example across midnight, the markup doesn't match. Give both sides the same instant:

  1. Pick an instant once per request, on the server.
  2. Send it to the client as a string, with the page's data.
  3. Pass it as initialNow on both sides.

initialNow is read once, when the calendar is created. After mounting, the client reads its clock and catches up to the real time. On the server the clock never starts.

// the same instant on the server and the client: pick it once per request and send it as a string
export const createForRequest = (now: string, date: string) =>
  createCalendar({
    timeZone,
    views,
    features,
    view: 'week',
    date: Temporal.PlainDate.from(date),
    events: bookings,
    onNavigate: () => {},
    initialNow: Temporal.Instant.from(now),
  });

Fetching on the server

getCalendarNavigation gives the visible range for any view and date without creating a calendar, so the server can fetch the events to render:

// the visible range for any view and date, without a calendar: for example, to fetch on the server
export const range = getCalendarNavigation(views, 'month', Temporal.PlainDate.from('2026-03-11'), {
  firstDayOfWeek: 1,
}).range;