API
Errors
The four error classes, what throws them and where they are caught for you.
The four classes
| Class | Type | Description |
|---|---|---|
InvalidEventError | Error | An event that cannot be read, or one that ends before it starts. Carries the offending eventId. |
InvalidRangeError | Error | An anchor date or time zone that cannot be read, a slotMinutes outside 1 to 1440, or a dayCount below one. |
InvalidRecurrenceError | Error | A rule part the engine does not support, rather than ignoring it. |
MissingTemporalError | Error | Reported immediately when globalThis.Temporal is absent. Chronous reads Temporal synchronously. |
tsx
import { InvalidEventError, buildCalendar } from '@midstem/chronous' import type { EventInput } from '@midstem/chronous' const events: EventInput[] = [ { id: 'standup', start: '2026-03-18T09:00', duration: 'PT30M' }, // ends before it starts { id: 'broken', start: '2026-03-18T15:00', end: '2026-03-18T14:00' }, ] try { buildCalendar( { view: 'day', currentDate: '2026-03-18', timeZone: 'Europe/Kyiv' }, events, ) } catch (error) { if (error instanceof InvalidEventError) { console.log(error.eventId) // 'broken' } } // One unusable event fails the whole call — nothing is drawn from half a list.
In React they are caught for you
useCalendar catches all four and hands them back as state, because a throw during render takes the whole tree down.
tsx
import { useCalendar } from '@midstem/chronous-react' import type { CalendarRange, EventInput } from '@midstem/chronous-react' // An unreadable time zone: InvalidRangeError rather than a bare RangeError const range: CalendarRange = { view: 'week', currentDate: '2026-03-18', timeZone: 'Europe/Kyviv', } const events: EventInput[] = [ { id: 'standup', start: '2026-03-18T09:00', duration: 'PT30M' }, ] export const Board = () => { const { calendar, error } = useCalendar(range, events) // calendar is null exactly when error is set if (error) return <p className="failed">{error.message}</p> return <p>{calendar.days.length} days drawn</p> }
Calendar.Root renders renderError inside its own element, so the layout does not collapse — and rethrows when no renderError is given, because an invalid range is a bug in the input and swallowing it into a blank grid hides it.
tsx
import { Calendar } from '@midstem/chronous-react' import type { CalendarError, CalendarRange, EventInput, } from '@midstem/chronous-react' const range: CalendarRange = { view: 'week', currentDate: '2026-03-18', timeZone: 'Europe/Kyiv', } const events: EventInput[] = [ { id: 'standup', start: '2026-03-18T09:00', duration: 'PT30M' }, ] const Failed = ({ error }: { error: CalendarError }) => ( <p className="failed">{error.message}</p> ) export const Board = () => ( <Calendar.Root className="calendar" range={range} events={events} // Without this, an unreadable range rethrows rather than drawing // a blank grid that hides the bug. renderError={error => <Failed error={error} />} > <Calendar.TimeGrid hourHeight={48}> <Calendar.DayColumns className="column"> <Calendar.TimedEvents className="event" /> </Calendar.DayColumns> </Calendar.TimeGrid> </Calendar.Root> )