Skip to content

kirilinsky/dateforge-react-calendar

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

469 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

downloads    codecov    publint    SSR safe    A11y    OpenSSF Scorecard    Socket    Visual regression    CodSpeed Badge

@dateforge/react-calendar

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-calendar

Styles 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 board

Compose 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+singleDate | null, day+multipleDate[], 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 axesmode: single / multiple / range / multi-range × unit: day / week / month. Plus minSpan/maxSpan, maxDates, maxRanges.
  • Disabled vs excludeddisabled days can't be picked; exclude days 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 modegradient on the root adds corner glows and gradient selected fills, pure CSS, theme-driven.
  • Scheme controlscheme="auto" | "light" | "dark", controllable via onSchemeChange; no dark-mode flash on SSR.
  • Locales via Intl — month/weekday names, digits, and week start derive from any BCP-47 locale; no locale files to import.
  • RTL — logical properties throughout; the calendar follows the inherited dir.
  • TimewithTime, 12/24-hour, ampmLabels, defaultTime, and a minTime/maxTime window enforced by the core.
  • Time zones & DST — IANA timeZone for "today" resolution, with explicit ambiguous/nonexistent-time policies.
  • Presets — built-in packs plus definePreset with validation context (disabled/min/max aware).
  • Accessibility — full keyboard navigation, focus management, live announcements, axe test suite in CI.
  • Validation hooksonValidationReject tells you what was refused and why; onViewChange tracks 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 …> became createCalendarConfig({ mode, locale, min, … }) passed as one config prop — compiled once, shared by every module.
  • Value shapes by unit × mode. onChange shapes are fully determined by the config's unit and mode (see above); segments and reasons arrive in a second details argument.
  • Themes are families only. Every theme is a { light, dark } pair; the v1 single-variant palettes are gone, createTheme always 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.

Links

License

MIT © Kirilinsky

About

Modular React calendar and date/time picker. Single, range, and multi-select with time support and presets. Themeable, accessible, SSR-safe, zero dependencies.

Resources

License

Code of conduct

Contributing

Security policy

Stars

21 stars

Watchers

1 watching

Forks

Packages

 
 
 

Contributors