Components
Calendar
Date selection — single days or ranges.
Single date
const [date, setDate] = useState<Date | undefined>(new Date());
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded-lg border border-separator"
/> Range
Presets
Schedule when this note goes live on mrkdwn.
const presets = [
{ label: "Today", date: () => new Date() },
{ label: "This week", date: () => startOfWeek(new Date(), { weekStartsOn: 1 }) },
{ label: "In a month", date: () => addMonths(new Date(), 1) },
];
<div className="flex gap-2">
{presets.map((preset) => (
<Button
key={preset.label}
variant="outline"
size="sm"
onClick={() => {
const next = preset.date();
setDate(next);
setMonth(next);
}}
>
{preset.label}
</Button>
))}
</div>
<Calendar
mode="single"
selected={date}
onSelect={setDate}
month={month}
onMonthChange={setMonth}
/> Month & year selector · booked dates
Struck days already have a publishing slot booked.
Two months · week numbers
{/* Horizontal layout */}
<Calendar mode="range" numberOfMonths={2} />
{/* Week numbers */}
<Calendar mode="single" showWeekNumber /> Min & max dates · date and time
Only the next 30 days are publishable.
{/* Bound the selectable window */}
<Calendar
mode="single"
disabled={{ before: today, after: addDays(today, 30) }}
/>
{/* Date + time picker */}
<Calendar mode="single" selected={date} onSelect={setDate} />
<input type="time" value={time} onChange={(e) => setTime(e.target.value)} />
Two months side by side is geist's Horizontal Layout; the wrapper stacks them vertically at
narrow widths (Stacked) via flex-col md:flex-row. Size, Compact
and Custom Cell Size are all the --cell-size CSS variable —
e.g. className="[--cell-size:--spacing(7)]" for a compact grid.
The Presets block above starts on “today”, which is Presets with default. For a Pinned
timezone / Timezone Selected Date, pass react-day-picker's
timeZone="America/New_York"; a Persian Calendar needs a
Persian locale plus Arabic-Indic
numerals.
Markdown for agents: /design/calendar.md · spec: /design.md