Examples
Week view
A full week with an all-day strip, a time grid, a now marker and a toolbar.
A week
A week with a toolbar, day headings, an all-day strip that never collapses, a time grid scrolled to eight in the morning and a recurring standup. This is the real component running on this page — the arrows step it, and the two blocks below are the file and the stylesheet that produced it.
Running here
The component
Week.tsx
import { createCalendarComponents } from '@midstem/chronous-react' import type { CalendarRange, EventInput } from '@midstem/chronous-react' import { useState } from 'react' type EventData = { title: string } const Calendar = createCalendarComponents<EventData>() const INITIAL: 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-16T09:00', duration: 'PT15M', recurrence: { rule: 'FREQ=WEEKLY;BYDAY=MO,WE,FR' }, data: { title: 'Standup' }, }, { id: 'review', start: '2026-03-18T14:00', end: '2026-03-18T15:30', data: { title: 'Design review' }, }, { id: 'pairing', start: '2026-03-18T14:30', end: '2026-03-18T16:00', data: { title: 'Pairing' }, }, ] export const Week = (): JSX.Element => { const [range, setRange] = useState<CalendarRange>(INITIAL) return ( <Calendar.Root className="calendar" range={range} events={EVENTS} locale="en-GB" gutterWidth="56px" > <Calendar.Toolbar className="toolbar" onNavigate={setRange}> {({ title, navigation, goTo }) => ( <> <button type="button" disabled={!navigation.prev} onClick={() => navigation.prev && goTo(navigation.prev)} > ← </button> <strong>{title}</strong> <button type="button" disabled={!navigation.next} onClick={() => navigation.next && goTo(navigation.next)} > → </button> </> )} </Calendar.Toolbar> <Calendar.Header className="header"> <Calendar.DayHeadings className="heading"> {({ weekdayLabel, dayLabel }) => ( <> <span>{weekdayLabel}</span> <strong>{dayLabel}</strong> </> )} </Calendar.DayHeadings> </Calendar.Header> <Calendar.AllDayRow className="all-day" minLanes={1} gutterCell={<span className="gutter-label">all-day</span>} > <Calendar.AllDayEvents className="bar"> {({ event }) => event.data?.title} </Calendar.AllDayEvents> </Calendar.AllDayRow> <Calendar.TimeGrid className="grid" hourHeight={48} scrollToHour={8}> <Calendar.TimeAxis> <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, box }) => ( <> {event.data?.title} {box.continuesAfter && ' ↓'} </> )} </Calendar.TimedEvents> </Calendar.DayColumns> </Calendar.TimeGrid> </Calendar.Root> ) }
The stylesheet
Nothing here positions anything: the grid columns, the height of a box and the lane of a bar all arrive as inline styles. What is left is colour, borders and type.
week.css
.calendar { display: flex; flex-direction: column; /* seven readable columns, or a horizontal scroll rather than a squeeze */ min-width: 660px; height: 640px; color: #d8e5ff; background: #020013; border: 1px solid rgba(255, 255, 255, 0.14); border-radius: 12px; overflow: hidden; } .toolbar { display: flex; align-items: center; gap: 12px; padding: 10px 12px; } .header, .all-day { border-bottom: 1px solid rgba(255, 255, 255, 0.14); } .heading { padding: 8px 0; text-align: center; font-size: 13px; border-left: 1px solid rgba(255, 255, 255, 0.06); } .all-day { padding: 4px 0; background: rgba(255, 255, 255, 0.02); } /* The gutter label belongs to the axis, not to the row: same size, same colour and the same right edge as the hour ticks under it. */ .gutter-label { display: block; padding-right: 8px; color: #8d8da0; font-size: 10px; line-height: 24px; text-align: right; } /* A time label is centred on its own line, so half of the first one sits above the grid. The padding is the room it hangs into — without it the all-day strip clips 00:00. */ .grid { flex: 1; padding-top: 10px; } .tick { right: 8px; font-size: 10px; color: #8d8da0; } .column { border-left: 1px solid rgba(255, 255, 255, 0.06); } .line { border-top: 1px solid rgba(255, 255, 255, 0.06); } .now { border-top: 2px solid #e67a56; } .event, .bar { padding: 0 6px; font-size: 11px; text-align: left; color: #0a0722; background: #aa90f8; border: none; border-radius: 6px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .bar { background: #8aa9f9; line-height: 24px; }
Worth noting
gutterWidthsits on the root because the header, the all-day row and the grid share one column template.minLanesof 1 holds the all-day strip open on weeks that have no all-day event, so the grid below it does not jump as you step. Try the arrows above.as="button"makes each event a real button — focusable, and keyboard-operable — without changing anything else.- The standup is one event with a rule, expanded only for the week on screen. See Recurrence.
NowMarkerdraws only in the column that is today, so it stays hidden while the range is anchored on March 2026.