← demo

Overview

react-calendar-datetime is an ultra-lightweight Date & Time picker for React — zero dependencies, fluid adaptive layout, and 20 built-in themes.

bundle~10 kb gzipped
dependencies0
themes20 (dark + light)
locales200+ via Intl API
react^18 || ^19
selection modessingle · range · multi
layouttwo months · fully modular · wide · column
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-datetime

No 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.

Mon
Tue
Wed
Thu
Fri
Sat
Sun
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.

Mon
Tue
Wed
Thu
Fri
Sat
Sun
 – 
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

1st clicksets from, clears to
hoverlive preview of potential range
2nd clicksets to — range complete
click from againresets both to null
rangeMinDaysshorter spans are blocked
rangeMaxDaysdays beyond limit are dimmed

Multi-select

Set mode="multiple". Each click toggles the date. Use max to cap the selection; further clicks are silently ignored once reached.

Mon
Tue
Wed
Thu
Fri
Sat
Sun
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) => void

Single mode only. Fires on select; receives null when the user deselects the active date.

onDatesChange(dates: Date[]) => void

Multiple mode only. Fires with the full updated selection array on every click.

onRangeChange(range: { from: Date|null; to: Date|null }) => void

Range mode only. Fires on each click. to is null while the end date has not yet been picked.

startDateDate

Minimum selectable and navigable date. Days before this date are dimmed (or hidden if hideLimited is true).

endDateDate

Maximum selectable and navigable date. Days after this date are dimmed (or hidden if hideLimited is true).

startMonthDate

Initial 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: 1

Day the week starts on. 0 = Sunday, 1 = Monday, 2 = Tuesday … 6 = Saturday.

disabledboolean | Date | { dayOfWeek } | { before, after } | { from, to } | array

One 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:

Mon
Tue
Wed
Thu
Fri
Sat
Sun
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.

maxnumber

mode='multiple' only. Caps the number of selectable dates. Omit for unlimited selection.

rangeMinDaysnumber

mode='range' only. Minimum span in calendar days. Selections shorter than this are blocked.

rangeMaxDaysnumber

mode='range' only. Maximum span in calendar days. Days beyond the limit are dimmed and unclickable.

showSelectedDatesbooleandefault: false

Renders 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: false

Show the current and next month side by side. Automatically stacks to a single column below ~540 px container width.

timebooleandefault: true

Time button in the header — opens a time popup on click.

timeGridbooleandefault: false

Full-size time picker panel rendered alongside the calendar (no popup).

monthsbooleandefault: true

Previous/next month navigation arrows in the header.

yearsbooleandefault: false

Previous/next year navigation arrows in the header.

monthsGridbooleandefault: false

Full-size month-grid panel alongside the calendar for fast month jumping.

compactMonthsbooleandefault: false

Compact month dropdown button in the header.

compactYearsbooleandefault: true

Compact year dropdown button in the header.

monthsColumnbooleandefault: false

Stack months vertically in two-months layout.

presetsbooleandefault: false

Quick-select preset buttons (Today, Tomorrow, This week, Next week, This month).

showWeekNumberbooleandefault: false

Display ISO week numbers alongside each row.

showHomeButtonbooleandefault: false

Home button in the header — becomes active when viewing any month other than the current one. Click navigates back without selecting a date.

showClearButtonbooleandefault: false

Clear 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:

Mon
Tue
Wed
Thu
Fri
Sat
Sun
Mon
Tue
Wed
Thu
Fri
Sat
Sun
show code
<Calendar
  value={date}
  onChange={setDate}
  twoMonthsLayout
  showWeekNumber
/>

Two months + stacked columns + month grid + range selection + selected dates panel:

Mon
Tue
Wed
Thu
Fri
Sat
Sun
May 2026
Mon
Tue
Wed
Thu
Fri
Sat
Sun
 – 
show code
<Calendar
  mode="range"
  value={range}
  onRangeChange={setRange}
  twoMonthsLayout
  monthsColumn
  monthsGrid
  showSelectedDates
/>

Week numbers + abbreviated month names:

Mon
Tue
Wed
Thu
Fri
Sat
Sun
14
15
16
17
18
19
show code
<Calendar
  value={date}
  onChange={setDate}
  theme="comfy"
  showWeekNumber
  shortMonths
/>

Month grid panel — click any month to jump straight to it:

Mon
Tue
Wed
Thu
Fri
Sat
Sun
show code
<Calendar
  value={date}
  onChange={setDate}
  monthsGrid
  compactYears
  time={false}
/>

Presets panel — Today, Tomorrow, This week, Next week, This month:

Mon
Tue
Wed
Thu
Fri
Sat
Sun
show code
<Calendar
  value={date}
  onChange={setDate}
  presets
/>

Inline time grid alongside the calendar:

Mon
Tue
Wed
Thu
Fri
Sat
Sun
18
15
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:

Mon
Tue
Wed
Thu
Fri
Sat
Sun
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

Mon
Tue
Wed
Thu
Fri
Sat
Sun
14
15
16
17
18
19
18
15
show full modular example
<Calendar
  value={date}
  onChange={setDate}
  theme="midnight"
  presets
  timeGrid
  showWeekNumber
  showHomeButton
  showClearButton
  compactMonths
  gradient
/>

Props — Appearance

gradientbooleandefault: false

Adds a subtle radial gradient tinted by the active theme's accent colour.

brutalismbooleandefault: false

Brutalism aesthetic — monospace font, hard borders, no border radius.

hour12booleandefault: false

12-hour AM/PM format for the time picker. Default is 24-hour.

highlightWeekendsbooleandefault: true

Visually highlights Saturday and Sunday in the day grid.

shortMonthsbooleandefault: false

Use abbreviated month names (Jan, Feb, Mar…) in headers.

Brutalism mode with gradient:

Mon
Tue
Wed
Thu
Fri
Sat
Sun
show code
<Calendar
  value={date}
  onChange={setDate}
  theme="industrial"
  brutalism
  gradient
/>

No weekend highlight, abbreviated months, gradient tint:

Mon
Tue
Wed
Thu
Fri
Sat
Sun
show code
<Calendar
  value={date}
  onChange={setDate}
  theme="neon"
  highlightWeekends={false}
  shortMonths
  gradient
/>

Props — Visibility

hideLimitedbooleandefault: false

Hide dates outside startDate/endDate entirely instead of dimming them.

hideDisabledbooleandefault: false

Hide disabled dates entirely instead of rendering them with a strikethrough.

hideWeekdaysbooleandefault: false

Hide the weekday header row (Mon Tue Wed…).

gesturesbooleandefault: true

Swipe 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:

Mon
Tue
Wed
Thu
Fri
Sat
Sun
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.

Mon
Tue
Wed
Thu
Fri
Sat
Sun
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:

Mon
Tue
Wed
Thu
Fri
Sat
Sun
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 colour

The 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.

en
Mon
Tue
Wed
Thu
Fri
Sat
Sun
de
Mo
Di
Mi
Do
Fr
Sa
So
zh
周一
周二
周三
周四
周五
周六
周日
ar
الاثنين
الثلاثاء
الأربعاء
الخميس
الجمعة
السبت
الأحد
fr
lun.
mar.
mer.
jeu.
ven.
sam.
dim.
sk
po
ut
st
št
pi
so
ne
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" />     // Polski

Pass 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.