Overview
react-calendar-datetime is an ultra-lightweight Date & Time picker for React — zero dependencies, fluid adaptive layout, and 20 built-in themes.
▶quick start
import { Calendar } from 'react-calendar-datetime'
<Calendar value={date} onChange={setDate} theme="carbon" />Install
npm i react-calendar-datetime
# or
pnpm add react-calendar-datetime
# or
yarn add react-calendar-datetimeNo peer dependencies beyond React itself. Works with React 18 and 19.
Single date
The default mode. Pass a Date to value and receive an updated one — or null when the user clicks the same day to deselect — via onChange.
▶show code
import { Calendar } from 'react-calendar-datetime'
import { useState } from 'react'
export default function App() {
const [date, setDate] = useState(new Date())
return (
<Calendar
value={date}
onChange={(d) => {
// d is Date | null — null when user deselects
if (d) setDate(d as Date)
}}
/>
)
}Date range
Set mode="range". The first click sets from, hover shows a live preview, the second click sets to. Clicking from again resets both. While picking the end date to is null.
▶show code
import { Calendar } from 'react-calendar-datetime'
import { useState } from 'react'
type Range = { from: Date | null; to: Date | null }
export default function App() {
const today = new Date()
const [range, setRange] = useState<Range>({
from: today,
to: new Date(today.getFullYear(), today.getMonth(), today.getDate() + 2),
})
return (
<Calendar
mode="range"
value={range}
onRangeChange={(r) => setRange(r)}
rangeMinDays={2}
rangeMaxDays={30}
showSelectedDates
/>
)
}range click logic
Multi-select
Set mode="multiple". Each click toggles the date. Use max to cap the selection; further clicks are silently ignored once reached.
▶show code
import { Calendar } from 'react-calendar-datetime'
import { useState } from 'react'
export default function App() {
const today = new Date()
const [dates, setDates] = useState<Date[]>([
today,
new Date(today.getFullYear(), today.getMonth(), today.getDate() + 2),
new Date(today.getFullYear(), today.getMonth(), today.getDate() + 5),
])
return (
<Calendar
mode="multiple"
value={dates}
onDatesChange={(d) => setDates(d)}
max={5} // optional cap
showSelectedDates // chips panel below calendar
/>
)
}Props — Data & callbacks
valueDate | Date[] | { from: Date|null; to: Date|null }Current value. Single mode: Date. Multi mode: Date[]. Range mode: { from, to } object.
onChange(date: Date | null) => voidSingle mode only. Fires on select; receives null when the user deselects the active date.
onDatesChange(dates: Date[]) => voidMultiple mode only. Fires with the full updated selection array on every click.
onRangeChange(range: { from: Date|null; to: Date|null }) => voidRange mode only. Fires on each click. to is null while the end date has not yet been picked.
startDateDateMinimum selectable and navigable date. Days before this date are dimmed (or hidden if hideLimited is true).
endDateDateMaximum selectable and navigable date. Days after this date are dimmed (or hidden if hideLimited is true).
startMonthDateInitial month to display when the calendar first renders. Does not select a date.
localestringdefault: 'en'Any valid BCP 47 locale tag — 'en', 'de', 'zh-CN', 'ar-SA', 'pt-BR', etc. Powered by the native Intl API.
themeCalendarThemedefault: 'paper'One of 20 built-in themes. See the Themes section.
widthstring | numberdefault: '100%'Any CSS width value ('100%', 370, '480px'). The layout adapts fluidly — font sizes and spacing scale with the container.
startOfWeek0–6default: 1Day the week starts on. 0 = Sunday, 1 = Monday, 2 = Tuesday … 6 = Saturday.
disabledboolean | Date | { dayOfWeek } | { before, after } | { from, to } | arrayOne or more disabled date rules. Pass true to disable everything, a Date for a single day, or rule objects for weekdays, before/after bounds, or ranges. See the Disabled Dates section.
Pick which day the week starts on:
▶show code
<Calendar
value={date}
onChange={setDate}
startOfWeek={1} // 0 = Sun, 1 = Mon, 6 = Sat
/>Props — Selection modes
mode'single' | 'multiple' | 'range'default: 'single'Controls selection behaviour.
maxnumbermode='multiple' only. Caps the number of selectable dates. Omit for unlimited selection.
rangeMinDaysnumbermode='range' only. Minimum span in calendar days. Selections shorter than this are blocked.
rangeMaxDaysnumbermode='range' only. Maximum span in calendar days. Days beyond the limit are dimmed and unclickable.
showSelectedDatesbooleandefault: falseRenders a panel below the calendar showing selected dates (single), a chip list (multi), or the from–to range (range).
▶show examples
// Single — default, no extra props needed
<Calendar value={date} onChange={setDate} />
// Range with 2–14 day constraint
<Calendar
mode="range"
value={range}
onRangeChange={setRange}
rangeMinDays={2}
rangeMaxDays={14}
showSelectedDates
/>
// Multi with 3-date cap
<Calendar
mode="multiple"
value={dates}
onDatesChange={setDates}
max={3}
showSelectedDates
/>Props — Layout modules
Every panel is opt-in. Combine them freely — they compose without conflict.
twoMonthsLayoutbooleandefault: falseShow the current and next month side by side. Automatically stacks to a single column below ~540 px container width.
timebooleandefault: trueTime button in the header — opens a time popup on click.
timeGridbooleandefault: falseFull-size time picker panel rendered alongside the calendar (no popup).
monthsbooleandefault: truePrevious/next month navigation arrows in the header.
yearsbooleandefault: falsePrevious/next year navigation arrows in the header.
monthsGridbooleandefault: falseFull-size month-grid panel alongside the calendar for fast month jumping.
compactMonthsbooleandefault: falseCompact month dropdown button in the header.
compactYearsbooleandefault: trueCompact year dropdown button in the header.
monthsColumnbooleandefault: falseStack months vertically in two-months layout.
presetsbooleandefault: falseQuick-select preset buttons (Today, Tomorrow, This week, Next week, This month).
showWeekNumberbooleandefault: falseDisplay ISO week numbers alongside each row.
showHomeButtonbooleandefault: falseHome button in the header — becomes active when viewing any month other than the current one. Click navigates back without selecting a date.
showClearButtonbooleandefault: falseClear button in the header — active when any date is selected. Click clears the entire selection (single, multi, or range).
Two-months layout — stacks automatically on narrow containers:
▶show code
<Calendar
value={date}
onChange={setDate}
twoMonthsLayout
showWeekNumber
/>Two months + stacked columns + month grid + range selection + selected dates panel:
▶show code
<Calendar
mode="range"
value={range}
onRangeChange={setRange}
twoMonthsLayout
monthsColumn
monthsGrid
showSelectedDates
/>Week numbers + abbreviated month names:
▶show code
<Calendar
value={date}
onChange={setDate}
theme="comfy"
showWeekNumber
shortMonths
/>Month grid panel — click any month to jump straight to it:
▶show code
<Calendar
value={date}
onChange={setDate}
monthsGrid
compactYears
time={false}
/>Presets panel — Today, Tomorrow, This week, Next week, This month:
▶show code
<Calendar
value={date}
onChange={setDate}
presets
/>Inline time grid alongside the calendar:
▶show code
<Calendar
value={date}
onChange={setDate}
timeGrid
time={false}
hour12
/>showHomeButton navigates back to today's month without selecting. showClearButton clears any selection. years adds prev/next year arrows. compactMonths turns the month label into a dropdown:
▶show code
<Calendar
value={date}
onChange={setDate}
theme="phosphor"
showHomeButton
showClearButton
years
compactMonths
/>Everything on — presets, inline time grid, week numbers, home & clear buttons, month grid
▶show full modular example
<Calendar
value={date}
onChange={setDate}
theme="midnight"
presets
timeGrid
showWeekNumber
showHomeButton
showClearButton
compactMonths
gradient
/>Props — Appearance
gradientbooleandefault: falseAdds a subtle radial gradient tinted by the active theme's accent colour.
brutalismbooleandefault: falseBrutalism aesthetic — monospace font, hard borders, no border radius.
hour12booleandefault: false12-hour AM/PM format for the time picker. Default is 24-hour.
highlightWeekendsbooleandefault: trueVisually highlights Saturday and Sunday in the day grid.
shortMonthsbooleandefault: falseUse abbreviated month names (Jan, Feb, Mar…) in headers.
Brutalism mode with gradient:
▶show code
<Calendar
value={date}
onChange={setDate}
theme="industrial"
brutalism
gradient
/>No weekend highlight, abbreviated months, gradient tint:
▶show code
<Calendar
value={date}
onChange={setDate}
theme="neon"
highlightWeekends={false}
shortMonths
gradient
/>Props — Visibility
hideLimitedbooleandefault: falseHide dates outside startDate/endDate entirely instead of dimming them.
hideDisabledbooleandefault: falseHide disabled dates entirely instead of rendering them with a strikethrough.
hideWeekdaysbooleandefault: falseHide the weekday header row (Mon Tue Wed…).
gesturesbooleandefault: trueSwipe left/right on the day grid to change months. Swipe on time tracks to adjust hours/minutes.
Date bounds with startDate / endDate + hideLimited — only the next 20 days are visible:
▶show code
const today = new Date()
const in20 = new Date(today)
in20.setDate(today.getDate() + 20)
<Calendar
value={date}
onChange={setDate}
theme="latte"
startDate={today}
endDate={in20}
hideLimited // hides out-of-range days entirely
showClearButton
/>Disabled dates
Pass one rule or an array of rules to the disabled prop. All matching rules are applied simultaneously.
{ dayOfWeek: [0, 6] }Disable specific weekdays. 0 = Sunday, 6 = Saturday.new Date('2024-12-25')Disable a single specific date by passing a Date directly.{ before: new Date() }Disable all dates before the given date.{ after: endOfYear }Disable all dates after the given date.{ before: dateA, after: dateB }Combine before and after in one rule.{ from: dateA, to: dateB }Disable a contiguous date range.trueDisable the entire calendar (all dates).Use hideDisabled to remove disabled days from the grid entirely.
▶show code
// Block weekends + past dates
<Calendar
value={date}
onChange={setDate}
disabled={[
{ dayOfWeek: [0, 6] },
{ before: new Date() },
]}
/>
// Block a specific date + a closed range
<Calendar
value={date}
onChange={setDate}
disabled={[
new Date("2025-01-01"),
{ from: new Date("2025-08-01"), to: new Date("2025-08-15") },
]}
hideDisabled
/>
// Entire calendar disabled
<Calendar value={date} onChange={setDate} disabled={true} />Themes
20 built-in themes via the theme prop — 10 dark, 10 light. Swatches show background · accent. Click a theme to preview it live:
▶show code
<Calendar theme="carbon" value={date} onChange={setDate} />CSS variables
Each theme exposes four CSS custom properties you can override at the :root level or on any parent element.
--c-bBackground colour of the calendar--c-hAccent / highlight colour (selected dates, active elements)--c-cPrimary text colour--c-sBorder and separator colourThe gradient prop adds a radial tint derived from --c-h automatically.
▶show override example
/* Override the carbon theme to use an indigo accent */
:root {
--c-h: #6366f1;
}
/* Scope overrides to a single calendar instance */
.my-calendar-wrapper {
--c-b: #0f172a;
--c-h: #f59e0b;
--c-c: #f1f5f9;
--c-s: rgba(255, 255, 255, 0.08);
}
// Then wrap the Calendar
<div className="my-calendar-wrapper">
<Calendar value={date} onChange={setDate} theme="carbon" />
</div>Localization
Powered by the native Intl API — 200+ BCP 47 locales, zero extra bytes. Days, months, date labels, and the range separator all follow local standards automatically.
endezharfrsk▶show locale examples
<Calendar locale="en" /> // English (default)
<Calendar locale="de" /> // Deutsch
<Calendar locale="fr" /> // Français
<Calendar locale="ru" /> // Русский
<Calendar locale="zh-CN" /> // 中文 (简体)
<Calendar locale="zh-TW" /> // 中文 (繁體)
<Calendar locale="ja" /> // 日本語
<Calendar locale="ar-SA" /> // العربية
<Calendar locale="pt-BR" /> // Português (Brasil)
<Calendar locale="tr" /> // Türkçe
<Calendar locale="ko" /> // 한국어
<Calendar locale="pl" /> // PolskiPass any valid BCP 47 language tag, including regional variants. No configuration, no locale files to import.
Limitations
Output depends on the runtime's Intl data
All date and time formatting is delegated to the native Intl API. Exact output — punctuation, abbreviated month names, ordinals — may differ between Node versions, browsers, and operating systems. Avoid hardcoding expected strings in snapshot tests; use pattern matching instead.
No custom format strings
The calendar renders dates using Intl.DateTimeFormat internally. If you need a specific display pattern like 'DD/MM/YYYY' in a separate input, format it yourself using a dedicated formatter and pass it outside of the Calendar component.
twoMonthsLayout always shows current + next
The second panel always follows the first. You cannot render two arbitrary months — the layout is always a consecutive pair.
No SSR calendar state
The Calendar is a client component. If you render it on the server without a 'use client' boundary, you will get a hydration mismatch for the current date. Wrap it in dynamic(() => import(...), { ssr: false }) or a client boundary.
Node.js < 13
Older Node versions shipped with small-icu — only the 'en' locale was guaranteed. Node 13+ includes full ICU by default. On older versions, install the full-icu package separately and set NODE_ICU_DATA.