Mini calendar
StableA small month for sidebars and widgets, with up to three squares under days that have events.
Choosing a day calls onChange; the month pages with its own arrow buttons or the keys, and stops at min and max. It follows a new value from outside (an app’s Today button), so it can mirror the day a bigger view is showing. It is CalendarGrid at the small size, with one Tab stop.
Updated View source
Sidebar with an agenda
import { useState } from 'react'import { MiniCalendar, parseISODate } from '@rchursin/product/calendar'import { SampleBadge } from '@rchursin/product/shell/layout'
const agenda: Record<string, string[]> = { '2026-10-19': ['Sprint planning'], '2026-10-20': ['Pairing: retry backoff'], '2026-10-21': ['Design review', 'Agent eval run', 'Release notes'], '2026-10-22': ['1:1 with Ana'], '2026-10-27': ['Team demo', 'Cost report'], '2026-10-30': ['Quarterly review'],}const dayName = (iso: string, style: 'long' | 'short' = 'long') => new Intl.DateTimeFormat('en-US', { weekday: style, month: style, day: 'numeric' }).format(parseISODate(iso) ?? new Date(2026, 9, 21))
/** A sidebar month: squares under the days that have events, and the chosen day's list beside it. */export default function MiniCalendarBasic() { const [day, setDay] = useState('2026-10-21') const events = agenda[day] ?? []
Usage
import { MiniCalendar } from '@rchursin/product/calendar'<MiniCalendar value={day} onChange={setDay} markers={(iso) => counts.get(iso) ?? 0} />Examples
Week numbers and limits
ISO weeks down the side. Days outside the quarter are struck through and can’t be chosen.
import { useState } from 'react'import { MiniCalendar } from '@rchursin/product/calendar'
/** ISO week numbers down the side. Days outside this quarter are struck through and can't be chosen. */export default function MiniCalendarWeekNumbers() { const [day, setDay] = useState('2026-11-12')
return <MiniCalendar value={day} onChange={setDay} weekNumbers min="2026-10-01" max="2026-12-31" />}API
Mini calendar props
| Prop | Type | Default | Description |
|---|---|---|---|
valuerequired | ISODate | – | The chosen day (never empty). A new value from outside moves the month to it. |
onChangerequired | (date: ISODate) => void | – | A day was chosen. |
markers | (date: ISODate) => number | – | Event count for a date: up to three squares, and “2 events” in its name. |
weekNumbers | boolean | – | ISO week numbers down the side. |
min | ISODate | – | Earlier days can’t be chosen, the keys stop there, and Previous month is disabled in its month. The focused day is kept on or after it. |
max | ISODate | – | Later days can’t be chosen, the keys stop there, and Next month is disabled in its month. The focused day is kept on or before it. |
className | string | – | Classes on the wrapper. |
Accessibility
Keyboard
| Key | What it does |
|---|---|
| Left arrowRight arrow | Previous or next day (swapped in right-to-left layouts). |
| Up arrowDown arrow | The same weekday a week earlier or later. |
| HomeEnd | First or last day of the week, in the dates locale (Sunday or Monday first; see setDateLocale). |
| Page UpPage Down | The same day in the previous or next month, clamped (Mar 31 goes to Feb 28). |
| ShiftPage UpShiftPage Down | The same day a year earlier or later. |
| EnterSpace | Chooses the focused day. |
| Tab | Previous and next month buttons, then the grid (one stop). |
- The month title is a polite live region and names the grid, so paging is announced.
- Each day says its event count (“2 events”); the squares are decorative.
- Narrow weekday letters are shown, but each header carries the full name for screen readers.
- At min or max the month button that would leave the range is disabled; if it had focus, focus moves to the other month button.
Guidelines
Do
- Use it next to a bigger view (agenda, week) to jump between days.
- Keep markers to a count; the list next to it says what the events are.
Don’t
- Don’t use it as a form field; use the date picker, which has a label and a value.
In use
Related
- Calendar gridOne month as an ARIA grid with the APG date-picker keys. Every picker in the kit is built on it.
- Agenda widgetA mini month with event markers and the chosen day’s agenda, for a dashboard.
- CalendarThe whole calendar: week, day, month and agenda views, calendars told apart by pattern, and create, edit and delete.
- Agenda listEvents grouped by day under headings that can stick, earliest first, each with its time, calendar pattern and people.
Source
The component’s own files, exactly as they run on this site.
/** * MiniCalendar: a small month for sidebars and widgets, with up to three squares under days * that have events. Choosing a day calls onChange; the month pages on its own. * * <MiniCalendar value={day} onChange={setDay} markers={(iso) => counts.get(iso) ?? 0} /> * * Same keys as every grid here (arrows, Page keys, Home / End); one Tab stop. With min / max the * month buttons stop at the bounds (disabled, like CalendarPanel's) and the focused day is kept inside them. */import { useId, useRef, useState } from "react"import { cx } from "../../components/cx"import { formatMonth } from "../lib/dates"import { IconButton } from "../ui/button"import { CalendarGrid } from "./CalendarGrid"import { clampISO, parseISODate, shiftMonths, type ISODate } from "./dates"
export interface MiniCalendarProps { value: ISODate onChange: (date: ISODate) => void /** Event count for a date */