@dateforge/react-calendar
v3.1.1
Published
Modular React calendar toolkit. Compose single, multiple, range, time, presets, tracks, themes, appearances, and custom flows with zero dependencies.
Maintainers
Readme
@dateforge/react-calendar
🚀 Live Demo · 📖 Docs · 📚 Storybook
A modular React calendar toolkit that starts tiny and grows with your product.
Monolithic pickers ship the grid, the toolbar, the time picker, the presets, the layout opinions — and all the weight. DateForge ships only what you use: every module is its own bundle on its own subpath, sharing one calendar core.
- Zero runtime dependencies. React 18/19 peer, nothing else.
- SSR-safe. No hydration mismatches — server render tested in CI.
- Accessible. Keyboard-first, roving focus, axe-audited, WCAG-checked contrast in every built-in theme.
- Themeable. 28 light/dark theme families, 8 appearances, gradient mode, or bring your own tokens.
- Pay per module. A minimal calendar stays around 20 KB min+gzip; size budgets are enforced in CI.
Install
npm i @dateforge/react-calendarStyles ship with the components: with ESM (any bundler) the CSS is picked up automatically via the modules' own stylesheet imports. CJS consumers import the stylesheet once: import '@dateforge/react-calendar/style.css'.
10-second start
One import, zero composition:
import { useState } from "react";
import { SimpleCalendar } from "@dateforge/react-calendar/prebuilt";
export function Example() {
const [date, setDate] = useState<Date | null>(null);
return <SimpleCalendar value={date} onChange={setDate} />;
}Prebuilt gallery
Every prebuilt is a ready recipe over the same primitives, with plain-Date props (value, defaultValue, onChange, locale, min/max, disabled, theme, appearance, gradient, scheme):
import {
DatePicker,
MonthPicker,
MultiMonthCalendar,
SimpleCalendar,
} from "@dateforge/react-calendar/prebuilt";
<SimpleCalendar onChange={setDate} /> // header + day grid
<DatePicker allowClear onChange={setDate} /> // typed input + grid + Today
<MonthPicker onChange={setMonth} /> // year stepper + 12-month grid
<MultiMonthCalendar months={6} cols={3} mode="range" /> // 6-month range boardCompose your own
When a prebuilt stops fitting, drop one level down: Calendar takes a compiled config (from createCalendarConfig) and any mix of modules as children. The root is a CSS grid — cols on the root declares columns, col on a module places it.
import {
Calendar,
commonPresets,
createCalendarConfig,
} from "@dateforge/react-calendar";
import { CalendarDays } from "@dateforge/react-calendar/modules/days";
import { CalendarPresets } from "@dateforge/react-calendar/modules/presets";
import {
CalendarToolbar,
CalendarToolbarMonthTrigger,
CalendarToolbarNext,
CalendarToolbarPrev,
CalendarToolbarYearTrigger,
} from "@dateforge/react-calendar/modules/toolbar";
const config = createCalendarConfig({
mode: "range",
locale: "de-DE",
disabled: { weekends: true },
});
export function BookingCalendar() {
return (
<Calendar
config={config}
cols={3}
onChange={(range) => console.log(range)} // { start: Date, end: Date } | null
>
<CalendarToolbar>
<CalendarToolbarPrev />
<CalendarToolbarMonthTrigger />
<CalendarToolbarYearTrigger />
<CalendarToolbarNext />
</CalendarToolbar>
<CalendarPresets col={1} presets={commonPresets} />
<CalendarDays col={2} />
</Calendar>
);
}Remove a line, remove a feature. Add a module, add a workflow. Order in JSX is visual flow — no order props, no slots.
The onChange value shape is derived from unit × mode alone: day+single → Date | null, day+multiple → Date[], any single span (range, or week/month single) → { start, end } | null, any multi span → { start, end }[].
Modules
Import from @dateforge/react-calendar/modules, or per subpath (…/modules/days, …/modules/toolbar, …) for the smallest possible bundle.
| Module | Use it for |
| --- | --- |
| CalendarToolbar + primitives | Composable header: prev/next (unit-aware), month/year triggers and labels, Home, Apply, clock |
| CalendarDays | Classic month grid — single, multiple, range, multi-range; offset for multi-month boards |
| CalendarMonthsGrid / CalendarYearsGrid | Month/year picking or fast jumps |
| CalendarDaysTrack / CalendarMonthsTrack / CalendarYearsTrack | Physics-based scrollable strips for compact and mobile layouts |
| CalendarTimeWheel / CalendarMonthsWheel / CalendarYearsWheel | iOS-style drum pickers, range-bound aware (bound="from" \| "to") |
| CalendarManualInput | Typed, segment-based date entry with keyboard stepping |
| CalendarPresets | Shortcuts — commonPresets, relativePresets, or definePreset your own |
| CalendarSelectedDates | Selected-date chips with overflow and per-chip clear |
| CalendarInfo | Selection metrics, relative hints, empty text |
| CalendarLunar | Information-only lunar phase strip |
Features
- Selection axes —
mode:single/multiple/range/multi-range×unit:day/week/month. PlusminSpan/maxSpan,maxDates,maxRanges. - Disabled vs excluded —
disableddays can't be picked;excludedays are cut out of emitted spans (business-day flows), with segments reported in change details. - 28 theme families — each with light + dark:
abyss,aurora,bauhaus,chalk,crimson,cyber,dracula,eclipse,espresso,fjord,graphite,industrial,meadow,mint,monsoon,nebula,neon,noir,pearl,prism,riso,sandstone,slate,snow,solar,split,temporal,velvet. - 8 appearances (shape/spacing, independent of color):
zenith,airy,bubble,compact,loft,press,soft,square. - Gradient mode —
gradienton the root adds corner glows and gradient selected fills, pure CSS, theme-driven. - Scheme control —
scheme="auto" | "light" | "dark", controllable viaonSchemeChange; no dark-mode flash on SSR. - Locales via
Intl— month/weekday names, digits, and week start derive from any BCP-47locale; no locale files to import. - RTL — logical properties throughout; the calendar follows the inherited
dir. - Time —
withTime, 12/24-hour,ampmLabels,defaultTime, and aminTime/maxTimewindow enforced by the core. - Time zones & DST — IANA
timeZonefor "today" resolution, with explicit ambiguous/nonexistent-time policies. - Presets — built-in packs plus
definePresetwith validation context (disabled/min/max aware). - Accessibility — full keyboard navigation, focus management, live announcements, axe test suite in CI.
- Validation hooks —
onValidationRejecttells you what was refused and why;onViewChangetracks navigation.
Theming in one minute
import { Calendar, createTheme } from "@dateforge/react-calendar";
import { nebula } from "@dateforge/react-calendar/themes";
import { compact } from "@dateforge/react-calendar/appearances";
// Built-in: by name (generated stylesheet) or by object (tree-shaken)
<Calendar config={config} theme="dracula" />
<Calendar config={config} theme={nebula} appearance={compact} scheme="dark" />
// Custom: common tokens + per-mode overrides → a light/dark family
const brand = createTheme({
accent: "#2563eb",
range: "#22c55e",
light: { backdrop: "#f8fafc" },
dark: { backdrop: "#0f172a", text: "#f8fafc" },
});
<Calendar config={config} theme={brand} />Bundle size
Budgets from .size-limit.json, min+gzip, enforced on every commit (React excluded):
| Import | Budget |
| --- | --- |
| Calendar only | < 18 KB |
| Calendar + CalendarDays | < 21.5 KB |
| Calendar + CalendarDays + theme + appearance | < 25.5 KB |
| Calendar + Toolbar + CalendarDays | < 26 KB |
| SimpleCalendar (prebuilt, one import) | < 27 KB |
Dual ESM/CJS, sideEffects limited to CSS, subpath exports per module — bundlers drop everything you don't touch.
Migrating from v2
v3 is a clean rebuild. The three headline breaking changes:
- Config object instead of flat props.
<Calendar mode locale min …>becamecreateCalendarConfig({ mode, locale, min, … })passed as oneconfigprop — compiled once, shared by every module. - Value shapes by
unit×mode.onChangeshapes are fully determined by the config's unit and mode (see above); segments and reasons arrive in a seconddetailsargument. - Themes are families only. Every theme is a
{ light, dark }pair; the v1 single-variant palettes are gone,createThemealways returns a family.
Prefer not to migrate composition code by hand? The prebuilt components cover the common v2 setups with one import. Full details in DOCUMENTATION.md.
