Examples
Likely one of these fits your case.
These are just starting points. Mix the modules however you like — booking, dashboards, forms, scheduling, whatever your product needs. Storybook is the open playground; this page is finished recipes you can copy and tweak.
npm i @dateforge/react-calendarNeed it now
4 prebuilt calendars
Grab one, pass value/onChange, ship. One import, nothing to compose.
SimpleCalendarDatePickerMonthPickerQuarter board
Want something bigger
38 composed recipes
A Calendar shell plus only the modules the case needs — modes, tracks, presets, time, custom day rendering.
rangemultiplepresetstimerenderDay
Prebuilt
4 ready-made sets — one import, no composition.
The four below ship assembled. Import one from @dateforge/react-calendar/prebuilt, hand it value/onChange and it works — plain Date props, no modules to wire. They take the same shared props as everything else (locale, min/max, disabled, theme, appearance, scheme, gradient), so you can restyle without dropping down a level. Outgrow one and you compose the same primitives it is built from.
SimpleCalendar
- Use this when
- You need a working date picker in one line, today.
- What it demonstrates
- The flagship prebuilt: navigation header + day grid, plain-Date props. Same shared props everywhere —
locale,min/max,disabled,theme,appearance,scheme,gradient.
Code
import { useState } from "react";
import { SimpleCalendar } from "@dateforge/react-calendar/prebuilt";
export function SimpleCalendarExample() {
import { SimpleCalendar } from "@dateforge/react-calendar/prebuilt";
const [date, setDate] = useState<Date | null>(null);
return (
<SimpleCalendar value={date} onChange={setDate} />
// Dress it up without composing anything:
<SimpleCalendar
defaultValue={new Date()}
min={new Date()}
theme="noir"
appearance="zenith"
gradient
/>
);
}DatePicker
- Use this when
- Forms where users type the date as often as they click it.
- What it demonstrates
- Prebuilt with a typed, segment-based input above the grid plus a Today jump — keyboard-first entry, grid as fallback.
Code
import { useState } from "react";
import { DatePicker } from "@dateforge/react-calendar/prebuilt";
export function DatePickerExample() {
import { DatePicker } from "@dateforge/react-calendar/prebuilt";
const [date, setDate] = useState<Date | null>(null);
return (
<DatePicker value={date} onChange={setDate} />
// Rules work the same as everywhere else:
<DatePicker onChange={setDate} disabled={{ weekends: true }} />
// Clear button in the input is on by default — opt out:
<DatePicker onChange={setDate} allowClear={false} />
);
}MonthPicker
- Use this when
- Billing periods, campaign months, or season selectors.
- What it demonstrates
- Prebuilt month selector: year-stepping header + 12-month grid (
unit: "month"under the hood). Picking a month selects the whole month, reported as its first day.
Code
import { useState } from "react";
import { MonthPicker } from "@dateforge/react-calendar/prebuilt";
export function MonthPickerExample() {
import { MonthPicker } from "@dateforge/react-calendar/prebuilt";
const [month, setMonth] = useState<Date | null>(null);
// onChange reports the first day of the picked month (or null).
return (
<MonthPicker value={month} onChange={setMonth} />
);
}Quarter board
- Use this when
- Roadmaps, quarters, or long bookings that need several months at once.
- What it demonstrates
MultiMonthCalendar— a 3-month range board generated from one prop set; one shared selection drags across months.
Code
import { MultiMonthCalendar } from "@dateforge/react-calendar/prebuilt";
export function QuarterBoardExample() {
import { MultiMonthCalendar } from "@dateforge/react-calendar/prebuilt";
return (
<MultiMonthCalendar
months={3}
cols={3}
mode="range"
startMonth={new Date(2026, 6, 1)}
/>
);
}Composition
38 recipes — assembled from modules.
Same core, opened up: a Calendar shell around createCalendarConfig(), plus only the modules a case needs. This is where modes, tracks, presets, time and custom day rendering live.
The basics
- Use this when
- You're starting a new flow and just need a working date picker.
- What it demonstrates
- Bare minimum composition — Calendar shell + nav + days + selected dates.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function TheBasicsExample() {
const [basicDate, setBasicDate] = useState<Date | null>(new Date());
const config = createCalendarConfig();
return (
<Calendar config={config} value={basicDate} onChange={(value) => setBasicDate(value as Date | null)}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
</CalendarToolbar>
<CalendarDays />
<CalendarSelectedDates allowClear={false} />
</Calendar>
);
}Week picker
- Use this when
- Timesheets, weekly reports, or anything that snaps to whole weeks.
- What it demonstrates
unit: "week"— one click selects the whole week and emits a{ start, end }span.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays, CalendarInfo } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function WeekPickerExample() {
const config = createCalendarConfig({ unit: "week" });
const [week, setWeek] = useState<{ start: Date; end: Date } | null>(null);
return (
<Calendar config={config} value={week} onChange={(value) => setWeek(value as { start: Date; end: Date } | null)}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
</CalendarToolbar>
<CalendarDays weekNumbers />
<CalendarInfo showSummary rangeStyle="duration" />
</Calendar>
);
}Shift blocks
- Use this when
- Rotas, maintenance windows, or anything collecting several separate ranges.
- What it demonstrates
mode: "multi-range"withmaxRanges— several{ start, end }spans, chips with per-span removal.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
CalendarToolbarClear,
} from "@dateforge/react-calendar/modules/toolbar";
export function ShiftBlocksExample() {
const config = createCalendarConfig({ mode: "multi-range", maxRanges: 3 });
const [shifts, setShifts] = useState<{ start: Date; end: Date }[]>([]);
return (
<Calendar config={config} value={shifts} onChange={(value) => setShifts(value as { start: Date; end: Date }[])}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
<CalendarToolbarClear />
</CalendarToolbar>
<CalendarDays />
<CalendarSelectedDates allowClear allowClearPerChip allowNavigate />
</Calendar>
);
}Drag a range across a weekend
Business days
- Use this when
- SLAs, delivery estimates, or any flow counting working days only.
- What it demonstrates
exclude: { weekends: true }— weekends stay spannable but are cut from the emitted value;details.segmentsreports the working-day blocks.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays, CalendarInfo } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
CalendarToolbarClear,
} from "@dateforge/react-calendar/modules/toolbar";
export function BusinessDaysExample() {
const config = createCalendarConfig({
mode: "range",
exclude: { weekends: true }, // cut from emitted spans
excludedEndpointPolicy: "snap-inward", // or "reject"
});
const [range, setRange] = useState<{ start: Date; end: Date } | null>(null);
const [segments, setSegments] = useState<number | null>(null);
return (
<Calendar
config={config}
value={range}
onChange={(value, details) => {
setRange(value as { start: Date; end: Date } | null);
setSegments(details.segments?.length ?? null);
}}
>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
<CalendarToolbarClear />
</CalendarToolbar>
<CalendarDays highlightWeekends />
<CalendarInfo showSummary rangeStyle="days" />
</Calendar>
{segments !== null && <p>{segments} working-day block(s) in the selection</p>}
);
}German locale + labels
- Use this when
- Localized products where every visible and screen-reader string must match the language.
- What it demonstrates
localedrives names/digits/week start via Intl; thelabelsregistry localizes every aria-label in one place.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
CalendarToolbarClear,
} from "@dateforge/react-calendar/modules/toolbar";
export function GermanLocaleLabelsExample() {
const config = createCalendarConfig({ locale: "de-DE" });
return (
<Calendar
config={config}
value={date}
onChange={(value) => setDate(value as Date | null)}
labels={{
clear: "Löschen",
previousMonth: "Vorheriger Monat",
nextMonth: "Nächster Monat",
selectMonth: "Monat wählen",
selectYear: "Jahr wählen",
}}
>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
<CalendarToolbarClear />
</CalendarToolbar>
<CalendarDays weekNumbers weekLabel="KW" weekdayFormat="narrow" />
</Calendar>
);
}App-side scheme: light
Controlled scheme
- Use this when
- The calendar must follow your app's own light/dark state.
- What it demonstrates
- Controlled
scheme+onSchemeChange— the built-in toggle reports the next scheme instead of flipping itself.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
CalendarToolbarThemeToggle,
} from "@dateforge/react-calendar/modules/toolbar";
export function ControlledSchemeExample() {
const config = createCalendarConfig();
const [scheme, setScheme] = useState<"light" | "dark">("light");
return (
<Calendar
config={config}
value={date}
onChange={(value) => setDate(value as Date | null)}
scheme={scheme}
onSchemeChange={setScheme}
>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
<CalendarToolbarThemeToggle />
</CalendarToolbar>
<CalendarDays />
</Calendar>
);
}Pinned toolbar actions
- Use this when
- A crowded toolbar that must stay tidy at any width.
- What it demonstrates
- The default toolbar is a wrapping flex row — overflow wraps to the next line instead of escaping the container.
CalendarToolbarGroup push="end"pins the actions to the inline end regardless of what shares the row.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarClear,
CalendarToolbarGroup,
CalendarToolbarHome,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarPrev,
CalendarToolbarThemeToggle,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function PinnedToolbarActionsExample() {
const config = createCalendarConfig();
const [date, setDate] = useState<Date | null>(null);
return (
<Calendar config={config} value={date} onChange={(value) => setDate(value as Date | null)}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarYearTrigger />
<CalendarToolbarNext />
{/* Rides the right edge; wraps as one unit when space runs out */}
<CalendarToolbarGroup push="end">
<CalendarToolbarHome />
<CalendarToolbarClear />
<CalendarToolbarThemeToggle />
</CalendarToolbarGroup>
</CalendarToolbar>
<CalendarDays />
</Calendar>
);
}Stay booking
- Use this when
- Lodging or short-stay rentals where guests pick check-in and check-out.
- What it demonstrates
- Range mode with disabled past dates, quick-stay presets, a nights counter via CalendarInfo, and an animated summary.
Code
import { useMemo, useState } from "react";
import { Calendar, createCalendarConfig, createDisabled, type PresetInput } from "@dateforge/react-calendar";
import { CalendarDays, CalendarPresets, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
import { soft } from "@dateforge/react-calendar/appearances";
export function StayBookingExample() {
const [stayRange, setStayRange] = useState<{ start: Date; end: Date } | null>(null);
const noPast = useMemo(() => {
const today = new Date();
today.setHours(0, 0, 0, 0);
return createDisabled({ before: today });
}, []);
// Range mode wants range-kind presets: value + range, or getValue
// returning { from, to }. Plain date presets show up disabled here.
const stayPresets = useMemo<PresetInput[]>(
() => [
{ label: "Tonight", value: 0, range: 1 },
{
id: "next-weekend",
label: "Weekend",
getValue: ({ now }) => {
const sat = new Date(now);
const daysToSat = (6 - sat.getDay() + 7) % 7 || 7;
sat.setDate(sat.getDate() + daysToSat);
const sun = new Date(sat);
sun.setDate(sat.getDate() + 1);
return { from: sat, to: sun };
},
},
{ label: "Week stay", value: 0, range: 6 },
{ label: "Two weeks", value: 0, range: 13 },
],
[],
);
const config = createCalendarConfig({ mode: "range", disabled: noPast });
return (
<Calendar config={config} value={stayRange} onChange={(value) => setStayRange(value as { start: Date; end: Date } | null)} appearance={soft}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
<CalendarToolbarHome />
<CalendarToolbarClear />
</CalendarToolbar>
<CalendarDays />
<CalendarPresets presets={stayPresets} />
<CalendarInfo showSummary rangeStyle="duration" />
<CalendarSelectedDates allowClear allowNavigate />
</Calendar>
);
}Flight search
- Use this when
- Booking flow needing departure and return without a full second month grid.
- What it demonstrates
- Split bound tracks (
bound="from"/bound="to") for compact range selection across two columns.
Code
import { useMemo, useState } from "react";
import { Calendar, createCalendarConfig, createDisabled } from "@dateforge/react-calendar";
import { CalendarDaysTrack, CalendarMonthsTrack, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarClear,
CalendarToolbarGroup,
CalendarToolbarLabel,
CalendarToolbarMonthLabel,
CalendarToolbarYearLabel,
} from "@dateforge/react-calendar/modules/toolbar";
import { temporal } from "@dateforge/react-calendar/themes";
import { compact } from "@dateforge/react-calendar/appearances";
export function FlightSearchExample() {
// Seed a range — with an empty selection both bound tracks
// fall back to the shared view date and look identical.
const [flightRange, setFlightRange] = useState<{ start: Date; end: Date } | null>(() => {
const start = new Date();
start.setDate(start.getDate() + 7);
const end = new Date();
end.setDate(end.getDate() + 14);
return { start, end };
});
const noPast = useMemo(() => {
const today = new Date();
today.setHours(0, 0, 0, 0);
return createDisabled({ before: today });
}, []);
const config = createCalendarConfig({ mode: "range", disabled: noPast });
return (
<Calendar config={config} value={flightRange} onChange={(value) => setFlightRange(value as { start: Date; end: Date } | null)} theme={temporal} appearance={compact}>
{/* Labels follow the range bounds — same dates the tracks below edit */}
<CalendarToolbar col="full" cols={2}>
<CalendarToolbarGroup col={1}>
<CalendarToolbarLabel>Departure</CalendarToolbarLabel>
<CalendarToolbarMonthLabel bound="from" />
<CalendarToolbarYearLabel bound="from" />
</CalendarToolbarGroup>
<CalendarToolbarGroup col={1}>
<CalendarToolbarLabel>Return</CalendarToolbarLabel>
<CalendarToolbarMonthLabel bound="to" />
<CalendarToolbarYearLabel bound="to" />
<CalendarToolbarClear />
</CalendarToolbarGroup>
</CalendarToolbar>
<CalendarMonthsTrack bound="from" short />
<CalendarDaysTrack bound="from" />
<CalendarMonthsTrack bound="to" short />
<CalendarDaysTrack bound="to" />
<CalendarSelectedDates col="full" allowClear />
</Calendar>
);
}Two-month stay search
- Use this when
- Desktop booking with side-by-side months and a single shared range.
- What it demonstrates
cols={2}with twoCalendarDays(offset 0 and 1) and one continuous range value.
Code
import { useMemo, useState } from "react";
import { Calendar, createCalendarConfig, createDisabled } from "@dateforge/react-calendar";
import { CalendarDays, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function TwomonthStaySearchExample() {
const [twoMonthRange, setTwoMonthRange] = useState<{ start: Date; end: Date } | null>(null);
const noPast = useMemo(() => {
const today = new Date();
today.setHours(0, 0, 0, 0);
return createDisabled({ before: today });
}, []);
const config = createCalendarConfig({ mode: "range", disabled: noPast });
return (
<Calendar config={config} value={twoMonthRange} onChange={(value) => setTwoMonthRange(value as { start: Date; end: Date } | null)} cols={2}>
<CalendarToolbar col="full">
<CalendarToolbarPrev />
<CalendarToolbarMonthLabel />
<CalendarToolbarYearLabel />
<CalendarToolbarMonthLabel offset={1} />
<CalendarToolbarYearLabel offset={1} />
<CalendarToolbarNext />
</CalendarToolbar>
<CalendarDays col={1} />
<CalendarDays offset={1} col={1} />
<CalendarSelectedDates col="full" allowClear allowNavigate />
</Calendar>
);
}Six-month availability
- Use this when
- Showing read-only open slots across half a year.
- What it demonstrates
- Read-only multiple-mode with a 3-column 6-month grid and
defaultViewDate.
Code
import { useMemo } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
import { compact } from "@dateforge/react-calendar/appearances";
export function SixmonthAvailabilityExample() {
const sixMonthDates = useMemo(
() => [
new Date(2026, 4, 8),
new Date(2026, 4, 17),
new Date(2026, 5, 4),
new Date(2026, 5, 22),
new Date(2026, 6, 9),
new Date(2026, 6, 28),
new Date(2026, 7, 13),
new Date(2026, 7, 26),
new Date(2026, 8, 10),
new Date(2026, 8, 24),
new Date(2026, 9, 6),
new Date(2026, 9, 21),
],
[],
);
const config = createCalendarConfig({ mode: "multiple", readOnly: true });
return (
<Calendar config={config} value={sixMonthDates} initialView={calendarDate(2026, 5, 1)} cols={3} appearance={compact}>
<CalendarToolbar col={1}>
<CalendarToolbarMonthLabel />
<CalendarToolbarYearLabel />
</CalendarToolbar>
<CalendarToolbar col={1} offset={1}>
<CalendarToolbarMonthLabel />
<CalendarToolbarYearLabel />
</CalendarToolbar>
<CalendarToolbar col={1} offset={2}>
<CalendarToolbarMonthLabel />
<CalendarToolbarYearLabel />
</CalendarToolbar>
<CalendarDays col={1} />
<CalendarDays offset={1} col={1} />
<CalendarDays offset={2} col={1} />
<CalendarToolbar col={1} offset={3}>
<CalendarToolbarMonthLabel />
<CalendarToolbarYearLabel />
</CalendarToolbar>
<CalendarToolbar col={1} offset={4}>
<CalendarToolbarMonthLabel />
<CalendarToolbarYearLabel />
</CalendarToolbar>
<CalendarToolbar col={1} offset={5}>
<CalendarToolbarMonthLabel />
<CalendarToolbarYearLabel />
</CalendarToolbar>
<CalendarDays offset={3} col={1} />
<CalendarDays offset={4} col={1} />
<CalendarDays offset={5} col={1} />
</Calendar>
);
}Delivery slots
- Use this when
- Letting users pick several non-contiguous delivery dates with capacity.
- What it demonstrates
- Multiple mode with
maxDates, weekend + past disable rule, animated selected list.
Code
import { useMemo, useState } from "react";
import { Calendar, createCalendarConfig, createDisabled } from "@dateforge/react-calendar";
import { CalendarDays, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function DeliverySlotsExample() {
const [deliveryDates, setDeliveryDates] = useState<Date[]>([]);
const weekdaysOnly = useMemo(() => {
const today = new Date();
today.setHours(0, 0, 0, 0);
return createDisabled({ weekends: true, before: today });
}, []);
const config = createCalendarConfig({
mode: "multiple",
maxDates: 4,
disabled: weekdaysOnly,
});
return (
<Calendar config={config} value={deliveryDates} onChange={(value) => setDeliveryDates(value as Date[])}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger compact />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger />
</CalendarToolbar>
<CalendarDays />
<CalendarSelectedDates allowClear />
</Calendar>
);
}Limited drop window
- Use this when
- Launch signup with only a handful of valid days.
- What it demonstrates
hideOutOfRange+min/maxDate+createDisabledfor a tightly bounded picker.
Code
import { useMemo, useState } from "react";
import { Calendar, createCalendarConfig, createDisabled } from "@dateforge/react-calendar";
import { CalendarDays, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function LimitedDropWindowExample() {
const [dropDate, setDropDate] = useState<Date | null>(null);
const dropDisabled = useMemo(
() =>
createDisabled({
dates: [new Date("2026-07-12"), new Date("2026-07-15")],
weekdays: [0, 6],
}),
[],
);
const config = createCalendarConfig({
min: new Date("2026-07-10"),
max: new Date("2026-07-18"),
disabled: dropDisabled,
});
return (
<Calendar
config={config}
value={dropDate}
onChange={(value) => setDropDate(value as Date | null)}
initialView={calendarDate(2026, 7, 10)}
>
<CalendarToolbar>
<CalendarToolbarMonthLabel />
<CalendarToolbarYearLabel />
<CalendarToolbarClock />
</CalendarToolbar>
<CalendarDays hideOutOfRange fixedWeeks={false} />
<CalendarSelectedDates allowClear />
</Calendar>
);
}Appointment booking
- Use this when
- Doctor, salon, or restaurant reservations needing date and time in one step.
- What it demonstrates
- Single mode +
CalendarTimeWheel+ nav withshowTime.
Code
import { useMemo, useState } from "react";
import { Calendar, createCalendarConfig, createDisabled } from "@dateforge/react-calendar";
import { CalendarDays, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import { CalendarTimeWheel } from "@dateforge/react-calendar/modules/time";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function AppointmentBookingExample() {
const [appointment, setAppointment] = useState<Date | null>(null);
const weekdaysOnly = useMemo(() => {
const today = new Date();
today.setHours(0, 0, 0, 0);
return createDisabled({ weekends: true, before: today });
}, []);
const config = createCalendarConfig({ withTime: true, disabled: weekdaysOnly });
return (
<Calendar gradient config={config} value={appointment} onChange={(value) => setAppointment(value as Date | null)}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
<CalendarToolbarClear />
</CalendarToolbar>
<CalendarDays />
<CalendarTimeWheel />
<CalendarSelectedDates allowClear showTime />
</Calendar>
);
}Analytics dashboard
- Use this when
- Filtering reports by familiar ranges (Today / Last 7 / Quarter).
- What it demonstrates
- Range mode +
CalendarPresetswith relative offsets + animated summary.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays, CalendarPresets, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function AnalyticsDashboardExample() {
const [reportRange, setReportRange] = useState<{ start: Date; end: Date } | null>(null);
// range: 0 → a single-day range, so "Today" stays clickable in range mode
const analyticsPresets = [
{ label: "Today", value: 0, range: 0 },
{ label: "Last 7 days", value: -6, range: 6 },
{ label: "Last 30 days", value: -29, range: 29 },
];
const config = createCalendarConfig({ mode: "range" });
return (
<Calendar config={config} value={reportRange} onChange={(value) => setReportRange(value as { start: Date; end: Date } | null)}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
<CalendarToolbarClear />
</CalendarToolbar>
<CalendarDays />
<CalendarPresets presets={analyticsPresets} />
<CalendarSelectedDates allowClear allowNavigate />
</Calendar>
);
}Support quick dates
- Use this when
- Reminders or follow-ups where "Tomorrow" or "Next Monday" covers most cases.
- What it demonstrates
- Single mode with custom presets, including dynamic ones via
getValue.
Code
import { useMemo, useState } from "react";
import { Calendar, createCalendarConfig, type PresetInput } from "@dateforge/react-calendar";
import { CalendarDays, CalendarPresets, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function SupportQuickDatesExample() {
const [singlePresetDate, setSinglePresetDate] = useState<Date | null>(null);
const supportPresets = useMemo<PresetInput[]>(
() => [
{ label: "Today", value: 0 },
{ label: "Tomorrow", value: 1 },
{ label: "In 3 days", value: 3 },
{
id: "next-monday",
label: "Next Monday",
getValue: ({ now }) => {
const date = new Date(now);
const delta = (8 - date.getDay()) % 7 || 7;
date.setDate(date.getDate() + delta);
return date;
},
},
],
[],
);
const config = createCalendarConfig();
return (
<Calendar config={config} value={singlePresetDate} onChange={(value) => setSinglePresetDate(value as Date | null)}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
<CalendarToolbarClear />
</CalendarToolbar>
<CalendarDays />
<CalendarPresets presets={supportPresets} />
<CalendarSelectedDates allowClear allowNavigate />
</Calendar>
);
}Holiday planner
- Use this when
- Marketing or seasonal planning around fixed and computed holidays.
- What it demonstrates
- Multiple mode with advanced custom presets (Christmas, Thanksgiving, Black Friday).
Code
import { useMemo, useState } from "react";
import { Calendar, createCalendarConfig, type PresetInput } from "@dateforge/react-calendar";
import { CalendarDays, CalendarMonthsGrid, CalendarPresets, CalendarSelectedDates, CalendarYearsGrid } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function HolidayPlannerExample() {
const [holidayRange, setHolidayRange] = useState<Date[]>([]);
function nthWeekdayOfMonth(year: number, month: number, weekday: number, occurrence: number) {
const date = new Date(year, month, 1);
const delta = (weekday - date.getDay() + 7) % 7;
date.setDate(1 + delta + (occurrence - 1) * 7);
return date;
}
const holidayPresets = useMemo<PresetInput[]>(
() => [
{
id: "new-year",
label: "New Year",
getValue: ({ now }) => {
const year =
now.getMonth() > 0 || now.getDate() > 1
? now.getFullYear() + 1
: now.getFullYear();
const date = new Date(year, 0, 1);
return date;
},
},
{
id: "independence-day",
label: "Independence Day",
getValue: ({ now }) => {
const year =
now.getMonth() > 6 || (now.getMonth() === 6 && now.getDate() > 4)
? now.getFullYear() + 1
: now.getFullYear();
const date = new Date(year, 6, 4);
return date;
},
},
{
id: "christmas-day",
label: "Christmas Day",
getValue: ({ now }) => {
const year =
now.getMonth() > 11 || (now.getMonth() === 11 && now.getDate() > 25)
? now.getFullYear() + 1
: now.getFullYear();
const date = new Date(year, 11, 25);
return date;
},
},
{
id: "thanksgiving-day",
label: "Thanksgiving",
getValue: ({ now }) => {
const year = now.getMonth() > 10 ? now.getFullYear() + 1 : now.getFullYear();
const date = nthWeekdayOfMonth(year, 10, 4, 4);
return date;
},
},
{
id: "christmas-eve",
label: "Christmas Eve",
getValue: ({ now }) => {
const year =
now.getMonth() > 11 || (now.getMonth() === 11 && now.getDate() > 24)
? now.getFullYear() + 1
: now.getFullYear();
const date = new Date(year, 11, 24);
return date;
},
},
{
id: "black-friday",
label: "Black Friday",
getValue: ({ now }) => {
const year = now.getMonth() > 10 ? now.getFullYear() + 1 : now.getFullYear();
const date = nthWeekdayOfMonth(year, 10, 4, 4);
date.setDate(date.getDate() + 1);
return date;
},
},
],
[],
);
const config = createCalendarConfig({ mode: "multiple" });
return (
<Calendar config={config} value={holidayRange} onChange={(value) => setHolidayRange(value as Date[])} cols={2}>
<CalendarPresets presets={holidayPresets} />
<CalendarDays />
<CalendarMonthsGrid col={1} />
<CalendarYearsGrid col={1} />
<CalendarSelectedDates col={2} allowClear allowNavigate />
</Calendar>
);
}Brand theme picker
- Use this when
- Branded checkout or onboarding where the picker has to match a custom palette in both light and dark.
- What it demonstrates
createThemewith shared tokens pluslight/darkvariants, and a built-in theme toggle to flip between them.
Code
import { useMemo, useState } from "react";
import { Calendar, createCalendarConfig, createTheme } from "@dateforge/react-calendar";
import { CalendarDays, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function BrandThemePickerExample() {
const [brandDate, setBrandDate] = useState<Date | null>(null);
// Shared tokens apply to both variants; light/dark override per mode.
const brandTheme = useMemo(
() =>
createTheme({
accent: "#7c3aed",
focusRing: "#ede9fe",
range: "#ddd6fe",
weekend: "#db2777",
light: { backdrop: "#faf5ff", tone: "#f3e8ff", text: "#3b0764", stroke: "#e9d5ff" },
dark: { backdrop: "#1a0b2e", tone: "#2e1065", text: "#f5f3ff", stroke: "#4c1d95" },
}),
[],
);
const config = createCalendarConfig();
return (
<Calendar config={config} value={brandDate} onChange={(value) => setBrandDate(value as Date | null)} theme={brandTheme}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
<CalendarToolbarThemeToggle />
</CalendarToolbar>
<CalendarDays />
<CalendarSelectedDates allowClear />
</Calendar>
);
}Branded preset rail
- Use this when
- A neutral calendar that still has to carry one branded surface — a dark preset rail, a colored info strip, a toolbar in product colors.
- What it demonstrates
- Per-module
themetaking acreateThemefamily object (v3.2) instead of only a built-in family name — the module gets inline--c-*vars while the rest of the calendar keeps the root theme.
Code
import { useMemo, useState } from "react";
import {
Calendar,
createCalendarConfig,
createTheme,
type PresetInput,
} from "@dateforge/react-calendar";
import { CalendarDays, CalendarInfo, CalendarPresets } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
import { snow } from "@dateforge/react-calendar/themes";
export function BrandedPresetRailExample() {
const [moduleThemeDate, setModuleThemeDate] = useState<Date | null>(null);
// Module theme is ModuleTheme = string | ThemeFamily.
const railTheme = useMemo(
() =>
createTheme({
accent: "#f97316",
range: "#fed7aa",
light: { backdrop: "#1c1917", tone: "#292524", text: "#fafaf9", stroke: "#44403c" },
dark: { backdrop: "#0c0a09", tone: "#1c1917", text: "#fafaf9", stroke: "#292524" },
}),
[],
);
const config = createCalendarConfig();
const supportPresets: PresetInput[] = [
{ label: "Today", value: 0 },
{ label: "Tomorrow", value: 1 },
{ label: "In 3 days", value: 3 },
];
// Root stays snow; only the presets rail and info strip take railTheme.
return (
<Calendar
config={config}
value={moduleThemeDate}
onChange={(value) => setModuleThemeDate(value as Date | null)}
theme={snow}
scheme="light"
cols={2}
>
<CalendarToolbar col={2}>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
</CalendarToolbar>
<CalendarPresets presets={supportPresets} theme={railTheme} />
<CalendarDays />
<CalendarInfo col={2} theme={railTheme} showSummary showRelative />
</Calendar>
);
}Dense product filter
- Use this when
- Compact dashboards where calendar rhythm needs to match dense data UI.
- What it demonstrates
createAppearancewith custom radius, spacing, font size, anddayRatio.
Code
import { useMemo, useState } from "react";
import { Calendar, createCalendarConfig, createAppearance } from "@dateforge/react-calendar";
import { CalendarDays, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function DenseProductFilterExample() {
const [denseRange, setDenseRange] = useState<{ start: Date; end: Date } | null>(null);
const denseAppearance = useMemo(
() =>
createAppearance({
radius: "5px",
spacing: "0.42em",
fontSize: "13px",
dayHeight: "2.2em",
transition: "120ms ease",
}),
[],
);
const config = createCalendarConfig({ mode: "range" });
return (
<Calendar config={config} value={denseRange} onChange={(value) => setDenseRange(value as { start: Date; end: Date } | null)} appearance={denseAppearance}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
<CalendarToolbarClear />
</CalendarToolbar>
<CalendarDays />
<CalendarSelectedDates allowClear />
</Calendar>
);
}Vacation request
- Use this when
- HR-style time off with min and max length rules.
- What it demonstrates
- Range mode with
minRangeDays/maxRangeDays, weekday-only rule, andCalendarInfoshowing the duration as the user drags.
Code
import { useMemo, useState } from "react";
import { Calendar, createCalendarConfig, createDisabled } from "@dateforge/react-calendar";
import { CalendarDays, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function VacationRequestExample() {
const [vacationRange, setVacationRange] = useState<{ start: Date; end: Date } | null>(null);
const weekdaysOnly = useMemo(() => {
const today = new Date();
today.setHours(0, 0, 0, 0);
return createDisabled({ weekends: true, before: today });
}, []);
const config = createCalendarConfig({
mode: "range",
disabled: weekdaysOnly,
minSpan: 2,
maxSpan: 21,
});
return (
<Calendar
config={config}
value={vacationRange}
onChange={(value) => setVacationRange(value as { start: Date; end: Date } | null)}
>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
<CalendarToolbarClear />
</CalendarToolbar>
<CalendarDays />
<CalendarInfo showSummary rangeStyle="duration" />
<CalendarSelectedDates allowClear />
</Calendar>
);
}Sprint planning
- Use this when
- Engineering planning around current sprint, next sprint, release week.
- What it demonstrates
- Range mode with custom-length presets (offset + range).
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays, CalendarPresets, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function SprintPlanningExample() {
const [sprintRange, setSprintRange] = useState<{ start: Date; end: Date } | null>(null);
const sprintPresets = [
{ label: "Current sprint", value: 0, range: 13 },
{ label: "Next sprint", value: 14, range: 13 },
{ label: "Release week", value: 28, range: 6 },
];
const config = createCalendarConfig({ mode: "range" });
return (
<Calendar config={config} value={sprintRange} onChange={(value) => setSprintRange(value as { start: Date; end: Date } | null)}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
</CalendarToolbar>
<CalendarPresets presets={sprintPresets} />
<CalendarDays />
<CalendarSelectedDates allowClear allowNavigate />
</Calendar>
);
}Invoice due date
- Use this when
- Billing form where users want to type or pick the date with a strict allowed window.
- What it demonstrates
CalendarManualInputpaired with the picker,locale, and min/max dates.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays, CalendarManualInput } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function InvoiceDueDateExample() {
const [manualDate, setManualDate] = useState<Date | null>(null);
const config = createCalendarConfig({
locale: "de-DE",
min: new Date("2026-05-01"),
max: new Date("2026-08-31"),
});
return (
<Calendar
config={config}
value={manualDate}
onChange={(value) => setManualDate(value as Date | null)}
>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
</CalendarToolbar>
<CalendarDays />
<CalendarManualInput allowClear />
</Calendar>
);
}Pick a year to browse the archive
Archive year browser
- Use this when
- Annual reports, archives, or timeline filters that only need year navigation.
- What it demonstrates
- Solo
CalendarYearsGridwithonYearSelectdriving external state.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarYearsGrid } from "@dateforge/react-calendar/modules";
export function ArchiveYearBrowserExample() {
const [archiveYear, setArchiveYear] = useState<Date | null>(null);
return (
<>
<Calendar
config={createCalendarConfig({
min: new Date("2018-01-01"),
max: new Date("2030-12-31"),
})}
initialView={calendarDate(2026, 1, 1)}
>
<CalendarYearsGrid
yearsPerPage={12}
onYearSelect={(year: number) => setArchiveYear(new Date(year, 0, 1))}
/>
</Calendar>
{archiveYear && <p>Browsing archive · {archiveYear.getFullYear()}</p>}
</>
);
}Pick a month to plan the campaign
Campaign month picker
- Use this when
- Lightweight season, campaign, or billing-period selectors.
- What it demonstrates
- Solo
CalendarMonthsGridwithonMonthSelectdriving external state.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarMonthsGrid } from "@dateforge/react-calendar/modules";
export function CampaignMonthPickerExample() {
const [campaignMonth, setCampaignMonth] = useState<Date | null>(null);
return (
<>
<Calendar
config={createCalendarConfig({
min: new Date("2026-01-01"),
max: new Date("2026-12-31"),
})}
initialView={calendarDate(2026, 5, 1)}
gradient
>
<CalendarMonthsGrid
short
onMonthSelect={(year: number, month: number) =>
setCampaignMonth(new Date(year, month - 1, 1))
}
/>
</Calendar>
{campaignMonth && (
<p>
Campaign ·{" "}
{campaignMonth.toLocaleString("en-US", { month: "long", year: "numeric" })}
</p>
)}
</>
);
}Pick a time slot
Time slot picker
- Use this when
- Slot pickers, reminders, or any flow where the date is fixed and only time matters.
- What it demonstrates
- Solo
CalendarTimeWheelwithtimeStep={{ minute: 10 }}for snapped slots.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarTimeWheel } from "@dateforge/react-calendar/modules/time";
export function TimeSlotPickerExample() {
const [meetingTime, setMeetingTime] = useState<Date | null>(null);
return (
<>
<Calendar
config={createCalendarConfig({ withTime: true })}
value={meetingTime}
onChange={(value) => setMeetingTime(value as Date | null)}
>
<CalendarTimeWheel />
</Calendar>
{meetingTime && (
<p>
Slot ·{" "}
{meetingTime.toLocaleTimeString("en-US", {
hour: "2-digit",
minute: "2-digit",
})}
</p>
)}
</>
);
}- New York—
- London—
- Berlin—
- Tokyo—
Global meeting time
- Use this when
- Scheduling one slot that teammates in different time zones can read at a glance.
- What it demonstrates
timeZone+hour12on the calendar, with the same instant rendered in four cities.
Code
import { useMemo, useState } from "react";
import { Calendar, createCalendarConfig, createDisabled } from "@dateforge/react-calendar";
import { CalendarDays, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import { CalendarTimeWheel } from "@dateforge/react-calendar/modules/time";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function GlobalMeetingTimeExample() {
const ZONES = [
{ city: "New York", tz: "America/New_York" },
{ city: "London", tz: "Europe/London" },
{ city: "Berlin", tz: "Europe/Berlin" },
{ city: "Tokyo", tz: "Asia/Tokyo" },
];
const [globalMeeting, setGlobalMeeting] = useState<Date | null>(null);
const noPast = useMemo(() => {
const today = new Date();
today.setHours(0, 0, 0, 0);
return createDisabled({ before: today });
}, []);
const config = createCalendarConfig({
withTime: true,
hour12: true,
timeZone: "America/New_York",
disabled: noPast,
});
return (
<Calendar
config={config}
value={globalMeeting}
onChange={(value) => setGlobalMeeting(value as Date | null)}
>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
<CalendarToolbarClear />
</CalendarToolbar>
<CalendarDays />
<CalendarTimeWheel />
</Calendar>
{globalMeeting && (
<ul>
{ZONES.map((z) => (
<li key={z.tz}>
<span>{z.city}</span>
<span>
{globalMeeting.toLocaleString("en-US", {
timeZone: z.tz,
weekday: "short",
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: true,
})}
</span>
</li>
))}
</ul>
)}
);
}Profile birthday
- Use this when
- Older dates where jumping years and months matters more than a month grid.
- What it demonstrates
- Track-based UI (
CalendarYearsTrack,CalendarMonthsTrack,CalendarDaysTrack).
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDaysTrack, CalendarMonthsTrack, CalendarSelectedDates, CalendarYearsTrack } from "@dateforge/react-calendar/modules";
import { bubble } from "@dateforge/react-calendar/appearances";
export function ProfileBirthdayExample() {
const [birthday, setBirthday] = useState<Date | null>(new Date(1994, 5, 14));
const config = createCalendarConfig();
return (
<Calendar config={config} value={birthday} onChange={(value) => setBirthday(value as Date | null)} appearance={bubble}>
<CalendarYearsTrack />
<CalendarMonthsTrack short />
<CalendarDaysTrack />
<CalendarSelectedDates allowClear />
</Calendar>
);
}Blackout calendar
- Use this when
- Operations calendars with weekends, maintenance windows, and exact blackout dates.
- What it demonstrates
- Range mode with composite
createDisabled(weekends + before + ranges + dates).
Code
import { useMemo, useState } from "react";
import { Calendar, createCalendarConfig, createDisabled } from "@dateforge/react-calendar";
import { CalendarDays, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function BlackoutCalendarExample() {
const [blackoutRange, setBlackoutRange] = useState<{ start: Date; end: Date } | null>(null);
// Everything matching a rule renders greyed out and unclickable:
// past days, weekends, the maintenance window, the exact date.
const blackout = createDisabled({
weekends: true,
before: new Date(),
ranges: [{ from: new Date("2026-06-10"), to: new Date("2026-06-14") }],
dates: [new Date("2026-06-20")],
});
const config = createCalendarConfig({ mode: "range", disabled: blackout });
return (
<Calendar config={config} value={blackoutRange} onChange={(value) => setBlackoutRange(value as { start: Date; end: Date } | null)}>
<CalendarToolbar>
<CalendarToolbarMonthTrigger compact />
<CalendarToolbarPrev unit="year" />
<CalendarToolbarYearTrigger />
<CalendarToolbarNext unit="year" />
<CalendarToolbarHome />
</CalendarToolbar>
<CalendarDays highlightWeekends />
<CalendarSelectedDates allowClear />
</Calendar>
);
}Launch day
- Use this when
- Locked launches, archive screens, or confirmed bookings.
- What it demonstrates
readOnlyflag plusallowNavigateon the selected dates display.
Code
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function LaunchDayExample() {
const launchDate = new Date(2026, 8, 9);
const config = createCalendarConfig({ readOnly: true });
return (
<Calendar config={config} value={launchDate}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthLabel />
<CalendarToolbarNext />
<CalendarToolbarYearLabel />
</CalendarToolbar>
<CalendarDays />
<CalendarSelectedDates allowNavigate />
</Calendar>
);
}Month wheel + day grid
- Use this when
- Compact pickers where month is spun via drum, day selected via grid.
- What it demonstrates
cols={2}, arrows navigate by year, wheel handles month, YearTrigger compact at right.
Code
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
export function MonthWheelDayGridExample() {
const config = createCalendarConfig();
return (
<Calendar config={config} value={date} onChange={(value) => setDate(value as Date | null)} cols={2}>
<CalendarToolbar col={2}>
<CalendarToolbarPrev unit="year" />
<CalendarToolbarYearTrigger />
<CalendarToolbarNext unit="year" />
</CalendarToolbar>
<CalendarMonthsWheel col={1} showLabel />
<CalendarDays col={1} />
</Calendar>
);
}Drum triggers in toolbar
- Use this when
- Compact headers where month and year are spun in a wheel popup instead of grids.
- What it demonstrates
compactonCalendarToolbarMonthTrigger/CalendarToolbarYearTrigger— the popup becomes an iOS-style drum picker.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function DrumTriggersInToolbarExample() {
const config = createCalendarConfig();
return (
<Calendar config={config} value={date} onChange={(value) => setDate(value as Date | null)}>
<CalendarToolbar>
<CalendarToolbarPrev />
{/* compact = drum-style wheel picker in the popup */}
<CalendarToolbarMonthTrigger compact />
<CalendarToolbarYearTrigger compact />
<CalendarToolbarNext />
</CalendarToolbar>
<CalendarDays />
</Calendar>
);
}Quarter-hour slots
- Use this when
- Call bookings or service slots that snap to 15-minute steps.
- What it demonstrates
CalendarTimeWheel step={{ minute: 15 }}— the minutes drum only offers 00 / 15 / 30 / 45;defaultTimeseeds the first pick.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays, CalendarSelectedDates } from "@dateforge/react-calendar/modules";
import { CalendarTimeWheel } from "@dateforge/react-calendar/modules/time";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function QuarterhourSlotsExample() {
const config = createCalendarConfig({
withTime: true,
defaultTime: { hour: 9 },
});
return (
<Calendar config={config} value={slot} onChange={(value) => setSlot(value as Date | null)}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
</CalendarToolbar>
<CalendarDays />
<CalendarTimeWheel step={{ minute: 15 }} labels="short" />
<CalendarSelectedDates showTime />
</Calendar>
);
}Lunar phase strip
- Use this when
- Astrology apps, farming calendars, tide trackers, or any domain where lunar phase is meaningful.
- What it demonstrates
CalendarLunarbelow the day grid — display-only, no interaction.
Code
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
export function LunarPhaseStripExample() {
import { CalendarLunar } from "@dateforge/react-calendar/modules/lunar";
const config = createCalendarConfig();
return (
<Calendar config={config} value={date} onChange={(value) => setDate(value as Date | null)}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
</CalendarToolbar>
<CalendarDays />
<CalendarLunar />
</Calendar>
);
}Weather forecast
- Use this when
- Trip planners or weather apps where each day shows an at-a-glance condition.
- What it demonstrates
CalendarDays renderDayreturning a custom cell — day number plus a per-day weather emoji.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function WeatherForecastExample() {
// Deterministic per-day value so each date always looks the same.
const seededRandom = (d: CalendarDate) => {
const seed = d.year * 10000 + d.month * 100 + d.day;
const x = Math.sin(seed) * 10000;
return x - Math.floor(x);
};
const WEATHER_ICONS = ["☀️", "⛅", "☁️", "🌧", "⛈", "❄️"];
const weatherFor = (d: CalendarDate) =>
WEATHER_ICONS[Math.floor(seededRandom(d) * WEATHER_ICONS.length)];
const config = createCalendarConfig();
return (
<Calendar config={config} value={date} onChange={(value) => setDate(value as Date | null)}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
</CalendarToolbar>
<CalendarDays
renderDay={(d, state) => {
if (state.outside) return <span>{d.day}</span>;
return (
<span style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 2, lineHeight: 1.1 }}>
<span style={{ fontSize: 13 }}>{d.day}</span>
<span aria-hidden style={{ fontSize: 13 }}>{weatherFor(d)}</span>
</span>
);
}}
/>
</Calendar>
);
}Activity heatmap
- Use this when
- Contribution graphs, habit trackers, or any view where each day carries an intensity.
- What it demonstrates
renderDaywith an absolute-positioned fill behind the number to tint each cell.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function ActivityHeatmapExample() {
const seededRandom = (d: CalendarDate) => {
const seed = d.year * 10000 + d.month * 100 + d.day;
const x = Math.sin(seed) * 10000;
return x - Math.floor(x);
};
const heatColor = (intensity: number) => {
const alpha = Math.min(0.85, 0.08 + intensity * 0.7);
return `rgba(34, 139, 60, ${alpha})`;
};
const config = createCalendarConfig();
return (
<Calendar config={config} value={date} onChange={(value) => setDate(value as Date | null)}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
</CalendarToolbar>
<CalendarDays
renderDay={(d, state) => {
if (state.outside) return <span>{d.day}</span>;
const intensity = seededRandom(d);
return (
<>
{/* Absolute fill overrides the .activeItem background so the
heatmap color wins on every appearance / border-radius. */}
<span aria-hidden style={{ position: "absolute", inset: 0, background: heatColor(intensity), borderRadius: "inherit" }} />
<span style={{ position: "relative", fontSize: 13 }}>{d.day}</span>
</>
);
}}
/>
</Calendar>
);
}Ticket prices
- Use this when
- Flight or event booking where users want to spot the cheapest day to buy.
- What it demonstrates
renderDayshowing a derived price under each day — green when cheap, red when pricey.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function TicketPricesExample() {
const seededRandom = (d: CalendarDate) => {
const seed = d.year * 10000 + d.month * 100 + d.day;
const x = Math.sin(seed) * 10000;
return x - Math.floor(x);
};
const priceFor = (d: CalendarDate) => {
const dow = new Date(d.year, d.month - 1, d.day).getDay();
const isWeekend = dow === 0 || dow === 6;
return Math.round(79 + seededRandom(d) * 220 + (isWeekend ? 60 : 0));
};
const config = createCalendarConfig();
return (
<Calendar config={config} value={date} onChange={(value) => setDate(value as Date | null)}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
</CalendarToolbar>
<CalendarDays
renderDay={(d, state) => {
if (state.outside) return <span>{d.day}</span>;
const price = priceFor(d);
const isCheap = price < 140;
return (
<span style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 2, lineHeight: 1.1 }}>
<span style={{ fontSize: 13 }}>{d.day}</span>
<span aria-hidden style={{ fontSize: 10, fontWeight: 600, color: isCheap ? "#15803d" : "#b91c1c" }}>
${price}
</span>
</span>
);
}}
/>
</Calendar>
);
}Event dots
- Use this when
- Schedules or agendas that mark how many events fall on a given day.
- What it demonstrates
renderDayrendering 1–3 dots under days that have events.
Code
import { useState } from "react";
import { Calendar, createCalendarConfig } from "@dateforge/react-calendar";
import { CalendarDays } from "@dateforge/react-calendar/modules";
import {
CalendarToolbar,
CalendarToolbarPrev,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
export function EventDotsExample() {
const seededRandom = (d: CalendarDate) => {
const seed = d.year * 10000 + d.month * 100 + d.day;
const x = Math.sin(seed) * 10000;
return x - Math.floor(x);
};
const EVENT_DAYS = new Set([3, 7, 14, 18, 22, 27]);
const eventCount = (d: CalendarDate) => {
if (!EVENT_DAYS.has(d.day)) return 0;
return 1 + Math.floor(seededRandom(d) * 3);
};
const config = createCalendarConfig();
return (
<Calendar config={config} value={date} onChange={(value) => setDate(value as Date | null)}>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarNext />
<CalendarToolbarYearTrigger compact />
</CalendarToolbar>
<CalendarDays
renderDay={(d, state) => {
if (state.outside) return <span>{d.day}</span>;
const count = eventCount(d);
return (
<span style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 2, lineHeight: 1.1 }}>
<span style={{ fontSize: 13 }}>{d.day}</span>
<span aria-hidden style={{ display: "flex", gap: 2, height: 4 }}>
{Array.from({ length: count }, (_, i) => (
<span key={i} style={{ width: 4, height: 4, borderRadius: "50%", background: "currentColor", opacity: 0.7 }} />
))}
</span>
</span>
);
}}
/>
</Calendar>
);
}