ChronousDocumentation

API

Calendar

Every part of the compound component, its props and the scope it hands to its children.

Calendar.Root

Everything else lives inside the root: it builds the calendar, holds the scope and renders one element of its own. The whole tree in one place:

tsx
import { createCalendarComponents } from '@midstem/chronous-react'
import type { CalendarRange, EventInput } from '@midstem/chronous-react'

type EventData = { title: string }

// Binds your event data to every render prop — see Typed event data below.
const Calendar = createCalendarComponents<EventData>()

const range: CalendarRange = {
  view: 'week',
  currentDate: '2026-03-18',
  timeZone: 'Europe/Kyiv',
}

const events: EventInput<EventData>[] = [
  {
    id: 'offsite',
    start: '2026-03-18',
    end: '2026-03-20',
    data: { title: 'Offsite' },
  },
  {
    id: 'standup',
    start: '2026-03-18T09:00',
    duration: 'PT30M',
    data: { title: 'Standup' },
  },
]

export const Board = () => (
  <Calendar.Root
    className="calendar"
    range={range}
    events={events}
    locale="en-GB"
  >
    <Calendar.Header className="grid-header">
      <Calendar.DayHeadings className="heading">
        {({ weekdayLabel, dayLabel }) => (
          <>
            <span>{weekdayLabel}</span> <strong>{dayLabel}</strong>
          </>
        )}
      </Calendar.DayHeadings>
    </Calendar.Header>

    <Calendar.AllDayRow gutterCell="all-day">
      <Calendar.AllDayEvents className="bar">
        {({ event }) => event.data?.title}
      </Calendar.AllDayEvents>
    </Calendar.AllDayRow>

    <Calendar.TimeGrid hourHeight={60}>
      <Calendar.TimeAxis className="gutter">
        <Calendar.TimeLabels className="tick" />
      </Calendar.TimeAxis>

      <Calendar.DayColumns className="column">
        <Calendar.TimeSlots className="line" />
        <Calendar.NowMarker className="now" />
        <Calendar.TimedEvents as="button" className="event">
          {({ event }) => event.data?.title}
        </Calendar.TimedEvents>
      </Calendar.DayColumns>
    </Calendar.TimeGrid>
  </Calendar.Root>
)
PropTypeDefaultDescription
rangeCalendarRange—What to draw. Required.
eventsreadonly EventInput<TData>[]—What to draw in it. Required.
localeLocaleId'en-US'The locale every built-in label is formatted in.
gutterWidthstring'3.25rem'The leading column shared by Header, AllDayRow and TimeGrid. Month and agenda ignore it.
renderError(error: CalendarError) => ReactNode—Rendered inside the root’s own element. Without it, an unreadable range or event rethrows.

Toolbar, header and all-day row

PartTypeDescription
Toolbar{ navigation, range, title, goTo }Wraps useCalendarNavigation and reports moves through onNavigate. Takes views to limit the view switcher.
Header{ calendar, range, locale, gutterWidth }The heading row, laid out as the shared grid. Takes gutterCell.
DayHeadings{ day, date, weekdayLabel, dayLabel, inCurrentPeriod }One element per day in the range.
AllDayRow{ row, laneHeight, lanes }The all-day strip. Takes laneHeight (24), minLanes (0) and gutterCell. Renders nothing when the range holds no all-day event and minLanes is 0.
AllDayEvents{ event, bar }One element per bar, positioned in its lane. Takes gap (4).

The time grid

Rendered for day, week and days — the views that carry slots.

PartTypeDescription
TimeGrid{ hourHeight, dayHeight }The scrolling grid. Takes hourHeight (60) and scrollToHour (7), which finds the nearest element that actually scrolls; pass null to leave the scroll alone.
TimeAxis{ hourHeight, dayHeight }The gutter column, sized to a full day.
TimeLabels{ slot, minuteOfDay, timeLabel }One element per slot, positioned down the axis.
DayColumns{ day }One column per day, each one a positioning context.
TimeSlots{ slot, minuteOfDay }One element per row inside a column.
NowMarker{ minuteOfDay }Rendered only in the column that is today, in the calendar’s zone. Ticks every thirty seconds.
TimedEvents{ event, box }One element per box, already packed into its column. Takes minHeight (22) and gap (3).

The month grid

PartTypeDescription
MonthGrid{ calendar, range, locale, gutterWidth }The month container.
MonthWeekdays{ day, weekdayLabel }One element per weekday, read off the first row.
MonthRows{ row, days, maxLanes, laneHeight }One element per week. Takes maxLanes (null) and laneHeight (20), both shared with its siblings.
MonthDays{ day, boxes, bars, hiddenBars, dayLabel, inCurrentPeriod, lanes }One cell per day in the row. hiddenBars is what maxLanes dropped.
MonthAllDayEvents{ event, bar }The bars of the row, up to maxLanes. Takes gap (4) and lanesTopOffset (28).
MonthTimedEvents{ event, box }The timed events of one cell, in order.

The agenda

PartTypeDescription
AgendaList{ calendar, range, locale, gutterWidth }The list container.
AgendaDays{ day, bars, boxes, weekdayLabel, dayLabel, monthLabel }One element per day that has something in it. Takes showEmptyDays (false) to render the whole span.
AgendaAllDayEvents{ event, bar }The all-day entries of that day.
AgendaTimedEvents{ event, box, timeRangeLabel }The timed entries, with a formatted range.

Scopes as hooks

Every scope is also a hook, so a component of your own can read it without a render prop. Reading one outside its parent throws and names the parent it wants.

HookTypeDescription
useCalendarContext{ calendar, range, locale, gutterWidth }Anywhere inside Calendar.Root.
useTimeGridContext{ hourHeight, dayHeight }Inside a time grid.
useDayColumnContext{ day }Inside a day column.
useAllDayContext{ row, laneHeight, lanes }Inside the all-day row.
useMonthRowContext{ row, days, maxLanes, laneHeight }Inside a month row.
useMonthDayContext{ day, boxes, bars, hiddenBars }Inside a month cell.
useAgendaDayContext{ day, bars, boxes }Inside an agenda day.
useNow(timeZone) => { date, minuteOfDay } | nullThe wall clock in a zone, for marking today yourself. Null until the first client tick.

createCalendarComponents<TData>() returns the same namespace with your event data typed on every render prop.