DatePicker
A date input with a calendar popover, plus a range variant
DatePicker composes Popover, Calendar, and a button-styled trigger into a ready-to-use date input. Two variants ship: DatePicker for a single date and DateRangePicker for a start–end range with a two-month preview.
Import
import { DatePicker, DateRangePicker } from '@nim-ui/components';Single Date
DatePicker supports both controlled (value + onChange) and uncontrolled (defaultValue) modes. The trigger shows the formatted date via date-fns.
Code
<DatePicker
value={date}
onChange={setDate}
name="bookingDate"
placeholder="Pick a booking date"
/>import { useState } from 'react';
import { DatePicker } from '@nim-ui/components';
export function BookingForm() {
const [date, setDate] = useState<Date | undefined>();
return (
<DatePicker
value={date}
onChange={setDate}
placeholder="Pick a date"
/>
);
}Custom Format
Pass any date-fns format string to control how the selected date is displayed in the trigger.
Code
<DatePicker value={date} onChange={setDate} format="yyyy-MM-dd" /><DatePicker value={date} onChange={setDate} format="yyyy-MM-dd" />
<DatePicker value={date} onChange={setDate} format="MMM d, yyyy" />
<DatePicker value={date} onChange={setDate} format="PPP" /> {/* default */}Buddhist Era Display
Use calendar="buddhist" when the field should display Thai Buddhist Era years. The selected value is still a normal JavaScript Date, and hidden form inputs still serialize as Gregorian ISO date strings (YYYY-MM-DD).
Code
<DatePicker
value={date}
onChange={setDate}
calendar="buddhist"
format="yyyy-MM-dd"
name="serviceDate"
/>
// Visible: 2569-06-16
// Hidden input: 2026-06-16Range Picker
DateRangePicker renders two months side by side and returns a DateRange ({ from, to }) object.
Code
<DateRangePicker
value={range}
onChange={setRange}
fromName="checkIn"
toName="checkOut"
/>import { useState } from 'react';
import { DateRangePicker } from '@nim-ui/components';
import type { DateRange } from 'react-day-picker';
export function StayRange() {
const [range, setRange] = useState<DateRange | undefined>();
return (
<DateRangePicker
value={range}
onChange={setRange}
placeholder="Check-in — Check-out"
/>
);
}Form Integration
DatePicker renders a hidden <input type="hidden" name={name}> with a Gregorian ISO date value (YYYY-MM-DD) whenever name is provided. That avoids UTC timezone shifts for date-only fields and works with native form submissions and libraries like react-hook-form via Controller. Pass an id to wire it up with FormField:
import { FormField, DatePicker } from '@nim-ui/components';
<FormField label="Event date" name="event-date">
<DatePicker id="event-date" name="eventDate" />
</FormField>For ranges, pass fromName and toName to render two hidden ISO inputs:
<DateRangePicker
value={range}
onChange={setRange}
fromName="dateFrom"
toName="dateTo"
/>Presets
Add operational presets for common report ranges:
Code
<DateRangePicker
presets={[
{ label: 'Today', value: todayRange },
{ label: 'Last 7 days', value: last7Days },
{ label: 'This month', value: thisMonth },
]}
/><DateRangePicker
presets={[
{ label: 'This week', value: { from: weekStart, to: weekEnd } },
{ label: 'Last 30 days', value: () => getLast30Days() },
]}
/>Uncontrolled
Skip value / onChange and pass defaultValue to let the component manage its own state.
<DatePicker defaultValue={new Date()} />Disabled
<DatePicker value={date} onChange={setDate} disabled />Props
DatePicker
| Name | Type | Default | Description |
|---|---|---|---|
value | Date | - | Controlled selected date |
defaultValue | Date | - | Uncontrolled initial date |
onChange | (date: Date | undefined) => void | - | Callback fired when the selected date changes |
placeholder | string | 'Pick a date' | Text shown in the trigger when no date is selected |
format | 'short' | 'medium' | 'long' | 'full' | 'iso' | string | 'PPP' | date-fns format string for the trigger label |
calendar | 'gregory' | 'buddhist' | 'gregory' | Display years as Gregorian CE or Buddhist Era; values remain Date objects |
locale | Locale | string | - | Display locale. String locales use Intl; date-fns Locale objects also localize the calendar UI |
formatDate | (date: Date) => string | - | Custom display formatter for product-specific policies |
disabled | boolean | false | Disable the trigger button |
id | string | - | Input id for FormField / label association |
name | string | - | Name attribute for native form submission (renders a hidden input with a YYYY-MM-DD ISO date value) |
className | string | - | Additional CSS classes for the trigger button |
DateRangePicker
| Name | Type | Default | Description |
|---|---|---|---|
value | DateRange | - | Controlled selected range ({ from, to }) |
defaultValue | DateRange | - | Uncontrolled initial range |
onChange | (range: DateRange | undefined) => void | - | Callback fired when the range changes |
placeholder | string | 'Pick a date range' | Text shown when no range is selected |
format | 'short' | 'medium' | 'long' | 'full' | 'iso' | string | 'LLL dd, y' | date-fns format string applied to both boundary dates |
calendar | 'gregory' | 'buddhist' | 'gregory' | Display years as Gregorian CE or Buddhist Era; hidden values remain YYYY-MM-DD ISO date strings |
locale | Locale | string | - | Display locale. String locales use Intl; date-fns Locale objects also localize the calendar UI |
formatDate | (date: Date) => string | - | Custom display formatter for product-specific policies |
disabled | boolean | false | Disable the trigger button |
id | string | - | Input id for FormField / label association |
fromName | string | - | Name attribute for the hidden start-date input (YYYY-MM-DD) |
toName | string | - | Name attribute for the hidden end-date input (YYYY-MM-DD) |
presets | DateRangePickerPreset[] | - | Preset buttons rendered beside the calendar |
showPresets | boolean | true | Show preset buttons when presets are provided |
className | string | - | Additional CSS classes for the trigger button |
Accessibility
- Trigger is a button — fully keyboard-focusable with the same focus ring as other nim-ui controls
- Popover manages focus: opens to the calendar grid, returns focus to the trigger on close
- Pass an
idand wire it to a<label htmlFor>(or useFormField) to announce the field to screen readers - Calendar inherits the full keyboard support documented in Calendar
- Hidden input (
nameprop) ensures the date is submitted natively without losing semantic meaning