React state management hooks for date picker components with internationalization and accessibility support.
npx @tessl/cli install tessl/npm-react-stately--datepicker@3.15.0@react-stately/datepicker is a React state management library that provides hooks for date picker components as part of Adobe's React Spectrum ecosystem. It offers cross-platform state management functionality through hooks that handle date selection, validation, formatting, and overlay state management with full internationalization support.
npm install @react-stately/datepickerimport {
useDatePickerState,
useDateFieldState,
useDateRangePickerState,
useTimeFieldState,
type DatePickerState,
type DatePickerStateOptions,
type DateFieldState,
type DateFieldStateOptions,
type DateRangePickerState,
type DateRangePickerStateOptions,
type TimeFieldState,
type TimeFieldStateOptions,
type DateSegment,
type SegmentType,
type FormatterOptions
} from "@react-stately/datepicker";For CommonJS:
const {
useDatePickerState,
useDateFieldState,
useDateRangePickerState,
useTimeFieldState
} = require("@react-stately/datepicker");import { useDatePickerState, useDateFieldState } from "@react-stately/datepicker";
import { CalendarDate, createCalendar } from "@internationalized/date";
function MyDatePicker() {
const state = useDatePickerState({
defaultValue: new CalendarDate(2023, 6, 15),
onChange: (value) => console.log("Selected:", value?.toString()),
granularity: 'day'
});
return (
<div>
<button onClick={() => state.setOpen(!state.isOpen)}>
{state.value?.toString() || "Select date"}
</button>
{state.isOpen && (
<div>Calendar would be rendered here</div>
)}
</div>
);
}
function MyDateField() {
const state = useDateFieldState({
locale: 'en-US',
createCalendar,
defaultValue: new CalendarDate(2023, 6, 15),
onChange: (value) => console.log("Field value:", value?.toString())
});
return (
<div>
{state.segments.map((segment, i) => (
<span key={i} aria-label={segment.type}>
{segment.text}
</span>
))}
</div>
);
}@react-stately/datepicker is built around several key components:
Core state management for date picker components that combine a text field with a calendar popover. Handles date selection, time selection, validation, and overlay state.
function useDatePickerState<T extends DateValue = DateValue>(
props: DatePickerStateOptions<T>
): DatePickerState;
interface DatePickerStateOptions<T extends DateValue> extends DatePickerProps<T> {
/**
* Determines whether the date picker popover should close automatically when a date is selected.
* @default true
*/
shouldCloseOnSelect?: boolean | (() => boolean);
}
interface DatePickerState extends OverlayTriggerState, FormValidationState {
/** The currently selected date. */
value: DateValue | null;
/** The default date. */
defaultValue: DateValue | null;
/** Sets the selected date. */
setValue(value: DateValue | null): void;
/**
* The date portion of the value. This may be set prior to `value` if the user has
* selected a date but has not yet selected a time.
*/
dateValue: DateValue | null;
/** Sets the date portion of the value. */
setDateValue(value: DateValue): void;
/**
* The time portion of the value. This may be set prior to `value` if the user has
* selected a time but has not yet selected a date.
*/
timeValue: TimeValue | null;
/** Sets the time portion of the value. */
setTimeValue(value: TimeValue): void;
/** The granularity for the field, based on the `granularity` prop and current value. */
granularity: Granularity;
/** Whether the date picker supports selecting a time, according to the `granularity` prop and current value. */
hasTime: boolean;
/** Whether the calendar popover is currently open. */
isOpen: boolean;
/** Sets whether the calendar popover is open. */
setOpen(isOpen: boolean): void;
/**
* The current validation state of the date picker, based on the `validationState`, `minValue`, and `maxValue` props.
* @deprecated Use `isInvalid` instead.
*/
validationState: ValidationState | null;
/** Whether the date picker is invalid, based on the `isInvalid`, `minValue`, and `maxValue` props. */
isInvalid: boolean;
/** Formats the selected value using the given options. */
formatValue(locale: string, fieldOptions: FieldOptions): string;
/** Gets a formatter based on state's props. */
getDateFormatter(locale: string, formatOptions: FormatterOptions): DateFormatter;
}State management for date field components with individually editable segments. Each part of a date value is displayed in a separate editable segment (year, month, day, etc.).
function useDateFieldState<T extends DateValue = DateValue>(
props: DateFieldStateOptions<T>
): DateFieldState;
interface DateFieldStateOptions<T extends DateValue = DateValue> extends DatePickerProps<T> {
/**
* The maximum unit to display in the date field.
* @default 'year'
*/
maxGranularity?: 'year' | 'month' | Granularity;
/** The locale to display and edit the value according to. */
locale: string;
/**
* A function that creates a Calendar object for a given calendar identifier.
* Such a function may be imported from the @internationalized/date package.
*/
createCalendar: (name: CalendarIdentifier) => Calendar;
}
interface DateFieldState extends FormValidationState {
/** The current field value. */
value: DateValue | null;
/** The default field value. */
defaultValue: DateValue | null;
/** The current value, converted to a native JavaScript Date object. */
dateValue: Date;
/** The calendar system currently in use. */
calendar: Calendar;
/** Sets the field's value. */
setValue(value: DateValue | null): void;
/** A list of segments for the current value. */
segments: DateSegment[];
/** A date formatter configured for the current locale and format. */
dateFormatter: DateFormatter;
/**
* The current validation state of the date field, based on the `validationState`, `minValue`, and `maxValue` props.
* @deprecated Use `isInvalid` instead.
*/
validationState: ValidationState | null;
/** Whether the date field is invalid, based on the `isInvalid`, `minValue`, and `maxValue` props. */
isInvalid: boolean;
/** The granularity for the field, based on the `granularity` prop and current value. */
granularity: Granularity;
/** The maximum date or time unit that is displayed in the field. */
maxGranularity: 'year' | 'month' | Granularity;
/** Whether the field is disabled. */
isDisabled: boolean;
/** Whether the field is read only. */
isReadOnly: boolean;
/** Whether the field is required. */
isRequired: boolean;
/** Increments the given segment. Upon reaching the minimum or maximum value, the value wraps around to the opposite limit. */
increment(type: SegmentType): void;
/** Decrements the given segment. Upon reaching the minimum or maximum value, the value wraps around to the opposite limit. */
decrement(type: SegmentType): void;
/**
* Increments the given segment by a larger amount, rounding it to the nearest increment.
* The amount to increment by depends on the field, for example 15 minutes, 7 days, and 5 years.
* Upon reaching the minimum or maximum value, the value wraps around to the opposite limit.
*/
incrementPage(type: SegmentType): void;
/**
* Decrements the given segment by a larger amount, rounding it to the nearest increment.
* The amount to decrement by depends on the field, for example 15 minutes, 7 days, and 5 years.
* Upon reaching the minimum or maximum value, the value wraps around to the opposite limit.
*/
decrementPage(type: SegmentType): void;
/** Sets the value of the given segment. */
setSegment(type: 'era', value: string): void;
setSegment(type: SegmentType, value: number): void;
/** Updates the remaining unfilled segments with the placeholder value. */
confirmPlaceholder(): void;
/** Clears the value of the given segment, reverting it to the placeholder. */
clearSegment(type: SegmentType): void;
/** Formats the current date value using the given options. */
formatValue(fieldOptions: FieldOptions): string;
/** Gets a formatter based on state's props. */
getDateFormatter(locale: string, formatOptions: FormatterOptions): DateFormatter;
}State management for date range picker components that allow users to select a start and end date. Combines two date fields with a range calendar popover.
function useDateRangePickerState<T extends DateValue = DateValue>(
props: DateRangePickerStateOptions<T>
): DateRangePickerState;
interface DateRangePickerStateOptions<T extends DateValue = DateValue> extends DateRangePickerProps<T> {
/**
* Determines whether the date range picker popover should close automatically when a date range is selected.
* @default true
*/
shouldCloseOnSelect?: boolean | (() => boolean);
}
interface DateRangePickerState extends OverlayTriggerState, FormValidationState {
/** The currently selected date range. */
value: RangeValue<DateValue | null>;
/** The default date range. */
defaultValue: DateRange | null;
/** Sets the selected date range. */
setValue(value: DateRange | null): void;
/** The date range portion of the value. This may be set prior to `value` if the user has selected dates but not times. */
dateRange: RangeValue<DateValue | null> | null;
/** Sets the date range portion of the value. */
setDateRange(value: DateRange): void;
/** The time range portion of the value. This may be set prior to `value` if the user has selected times but not dates. */
timeRange: RangeValue<TimeValue | null> | null;
/** Sets the time range portion of the value. */
setTimeRange(value: TimeRange): void;
/** Sets the date for either the start or end of the range. */
setDate(part: 'start' | 'end', value: DateValue | null): void;
/** Sets the time for either the start or end of the range. */
setTime(part: 'start' | 'end', value: TimeValue | null): void;
/** Sets the date and time for either the start or end of the range. */
setDateTime(part: 'start' | 'end', value: DateValue | null): void;
/** The granularity for the field, based on the `granularity` prop and current value. */
granularity: Granularity;
/** Whether the date range picker supports selecting a time, according to the `granularity` prop and current value. */
hasTime: boolean;
/** Whether the calendar popover is currently open. */
isOpen: boolean;
/** Sets whether the calendar popover is open. */
setOpen(isOpen: boolean): void;
/**
* The current validation state of the date range picker, based on the `validationState`, `minValue`, and `maxValue` props.
* @deprecated Use `isInvalid` instead.
*/
validationState: ValidationState | null;
/** Whether the date range picker is invalid, based on the `isInvalid`, `minValue`, and `maxValue` props. */
isInvalid: boolean;
/** Formats the selected value using the given options. */
formatValue(locale: string, fieldOptions: FieldOptions): {start: string, end: string} | null;
/** Gets a formatter based on state's props. */
getDateFormatter(locale: string, formatOptions: FormatterOptions): DateFormatter;
}State management for time field components that allow users to enter and edit time values. Each part of a time value is displayed in individually editable segments.
function useTimeFieldState<T extends TimeValue = TimeValue>(
props: TimeFieldStateOptions<T>
): TimeFieldState;
interface TimeFieldStateOptions<T extends TimeValue = TimeValue> extends TimePickerProps<T> {
/** The locale to display and edit the value according to. */
locale: string;
}
interface TimeFieldState extends DateFieldState {
/** The current time value as a Time object. */
timeValue: Time;
}// Base props interfaces from @react-types/datepicker
interface DatePickerProps<T extends DateValue> {
/** The current value (controlled). */
value?: T | null;
/** The default value (uncontrolled). */
defaultValue?: T | null;
/** Handler that is called when the value changes. */
onChange?: (value: MappedDateValue<T> | null) => void;
/** The minimum allowed date that a user may select. */
minValue?: DateValue;
/** The maximum allowed date that a user may select. */
maxValue?: DateValue;
/** Callback that is called for each date of the calendar. If true, the date is unavailable. */
isDateUnavailable?: (date: DateValue) => boolean;
/** Whether the calendar is disabled. */
isDisabled?: boolean;
/** Whether the calendar is read only. */
isReadOnly?: boolean;
/** A placeholder date that influences the format of the placeholder shown when no value is selected. */
placeholderValue?: T;
/** Determines the smallest unit that is displayed in the date picker. */
granularity?: Granularity;
/** Whether to hide the time zone abbreviation. */
hideTimeZone?: boolean;
/** Whether to display the time in 12 or 24 hour format. */
hourCycle?: 12 | 24;
/** Whether to always show leading zeros in the month, day, and hour fields. */
shouldForceLeadingZeros?: boolean;
/** Whether the element should receive focus on render. */
autoFocus?: boolean;
/** The name of the input element, used when submitting an HTML form. */
name?: string;
/** Whether user input is required on the input before form submission. */
isRequired?: boolean;
/** Whether the input value is invalid. */
isInvalid?: boolean;
/** The current validation state of the date picker. */
validationState?: ValidationState;
/** Controls the behavior of paging. */
pageBehavior?: 'visible' | 'single';
/** The day that starts the week. */
firstDayOfWeek?: 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat';
}
interface DateRangePickerProps<T extends DateValue> {
/** The current value (controlled). */
value?: RangeValue<T> | null;
/** The default value (uncontrolled). */
defaultValue?: RangeValue<T> | null;
/** Handler that is called when the value changes. */
onChange?: (value: RangeValue<MappedDateValue<T>> | null) => void;
/** The minimum allowed date that a user may select. */
minValue?: DateValue;
/** The maximum allowed date that a user may select. */
maxValue?: DateValue;
/** Callback that is called for each date of the calendar. If true, the date is unavailable. */
isDateUnavailable?: (date: DateValue) => boolean;
/** Whether non-contiguous ranges, i.e. ranges containing unavailable dates, may be selected. */
allowsNonContiguousRanges?: boolean;
/** Whether the calendar is disabled. */
isDisabled?: boolean;
/** Whether the calendar is read only. */
isReadOnly?: boolean;
/** A placeholder date that influences the format of the placeholder shown when no value is selected. */
placeholderValue?: T;
/** Determines the smallest unit that is displayed in the date picker. */
granularity?: Granularity;
/** Whether to hide the time zone abbreviation. */
hideTimeZone?: boolean;
/** Whether to display the time in 12 or 24 hour format. */
hourCycle?: 12 | 24;
/** Whether to always show leading zeros in the month, day, and hour fields. */
shouldForceLeadingZeros?: boolean;
/** Whether the element should receive focus on render. */
autoFocus?: boolean;
/** The name of the start date input element, used when submitting an HTML form. */
startName?: string;
/** The name of the end date input element, used when submitting an HTML form. */
endName?: string;
/** Whether user input is required on the input before form submission. */
isRequired?: boolean;
/** Whether the input value is invalid. */
isInvalid?: boolean;
/** The current validation state of the date range picker. */
validationState?: ValidationState;
/** Controls the behavior of paging. */
pageBehavior?: 'visible' | 'single';
/** The day that starts the week. */
firstDayOfWeek?: 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat';
}
interface TimePickerProps<T extends TimeValue> {
/** The current value (controlled). */
value?: T | null;
/** The default value (uncontrolled). */
defaultValue?: T | null;
/** Handler that is called when the value changes. */
onChange?: (value: MappedTimeValue<T> | null) => void;
/** Whether to display the time in 12 or 24 hour format. */
hourCycle?: 12 | 24;
/** Determines the smallest unit that is displayed in the time picker. */
granularity?: 'hour' | 'minute' | 'second';
/** Whether to hide the time zone abbreviation. */
hideTimeZone?: boolean;
/** Whether to always show leading zeros in the hour field. */
shouldForceLeadingZeros?: boolean;
/** A placeholder time that influences the format of the placeholder shown when no value is selected. */
placeholderValue?: T;
/** The minimum allowed time that a user may select. */
minValue?: TimeValue | null;
/** The maximum allowed time that a user may select. */
maxValue?: TimeValue | null;
/** Whether the field is disabled. */
isDisabled?: boolean;
/** Whether the field is read only. */
isReadOnly?: boolean;
/** Whether the element should receive focus on render. */
autoFocus?: boolean;
/** The name of the input element, used when submitting an HTML form. */
name?: string;
/** Whether user input is required on the input before form submission. */
isRequired?: boolean;
/** Whether the input value is invalid. */
isInvalid?: boolean;
/** The current validation state of the time picker. */
validationState?: ValidationState;
}// Date value types from @internationalized/date
type DateValue = CalendarDate | CalendarDateTime | ZonedDateTime;
type TimeValue = Time | CalendarDateTime | ZonedDateTime;
type Granularity = 'day' | 'hour' | 'minute' | 'second';
// Mapped types for controlled values
type MappedDateValue<T> =
T extends ZonedDateTime ? ZonedDateTime :
T extends CalendarDateTime ? CalendarDateTime :
T extends CalendarDate ? CalendarDate :
never;
type MappedTimeValue<T> =
T extends ZonedDateTime ? ZonedDateTime :
T extends CalendarDateTime ? CalendarDateTime :
T extends Time ? Time :
never;
// Calendar types from @internationalized/date
type CalendarIdentifier = 'buddhist' | 'ethiopic' | 'ethioaa' | 'coptic' | 'hebrew' | 'indian' | 'islamic-civil' | 'islamic-tbla' | 'islamic-umalqura' | 'japanese' | 'persian' | 'roc' | 'gregory';
interface Calendar {
identifier: CalendarIdentifier;
toDate(date: DateValue): Date;
fromDate(date: Date): CalendarDate;
getDaysInMonth(date: DateValue): number;
getMonthsInYear(date: DateValue): number;
getYearsInEra(date: DateValue): number;
getEras(): string[];
balanceDate(date: DateValue): CalendarDate;
}
interface DateFormatter {
formatToParts(date: Date): Intl.DateTimeFormatPart[];
format(date: Date): string;
formatRange(startDate: Date, endDate: Date): string;
formatRangeToParts(startDate: Date, endDate: Date): Intl.DateTimeFormatPart[];
}
// Core classes from @internationalized/date
interface CalendarDate {
readonly calendar: Calendar;
readonly era: string;
readonly year: number;
readonly month: number;
readonly day: number;
copy(): CalendarDate;
add(duration: DateDuration): CalendarDate;
subtract(duration: DateDuration): CalendarDate;
set(fields: DateFields): CalendarDate;
cycle(field: DateField, amount: number): CalendarDate;
toDate(timeZone: string): Date;
toString(): string;
compare(other: DateValue): number;
}
interface CalendarDateTime {
readonly calendar: Calendar;
readonly era: string;
readonly year: number;
readonly month: number;
readonly day: number;
readonly hour: number;
readonly minute: number;
readonly second: number;
readonly millisecond: number;
copy(): CalendarDateTime;
add(duration: DateTimeDuration): CalendarDateTime;
subtract(duration: DateTimeDuration): CalendarDateTime;
set(fields: DateTimeFields): CalendarDateTime;
cycle(field: DateField, amount: number): CalendarDateTime;
toDate(timeZone: string): Date;
toString(): string;
compare(other: DateValue): number;
}
interface ZonedDateTime {
readonly calendar: Calendar;
readonly era: string;
readonly year: number;
readonly month: number;
readonly day: number;
readonly hour: number;
readonly minute: number;
readonly second: number;
readonly millisecond: number;
readonly timeZone: string;
readonly offset: number;
copy(): ZonedDateTime;
add(duration: DateTimeDuration): ZonedDateTime;
subtract(duration: DateTimeDuration): ZonedDateTime;
set(fields: DateTimeFields): ZonedDateTime;
cycle(field: DateField, amount: number): ZonedDateTime;
toDate(): Date;
toString(): string;
compare(other: DateValue): number;
}
interface Time {
readonly hour: number;
readonly minute: number;
readonly second: number;
readonly millisecond: number;
copy(): Time;
add(duration: TimeDuration): Time;
subtract(duration: TimeDuration): Time;
set(fields: TimeFields): Time;
cycle(field: TimeField, amount: number): Time;
toString(): string;
compare(other: TimeValue): number;
}
// Duration and field types
interface DateDuration {
years?: number;
months?: number;
weeks?: number;
days?: number;
}
interface TimeDuration {
hours?: number;
minutes?: number;
seconds?: number;
milliseconds?: number;
}
interface DateTimeDuration extends DateDuration, TimeDuration {}
interface DateFields {
era?: string;
year?: number;
month?: number;
day?: number;
}
interface TimeFields {
hour?: number;
minute?: number;
second?: number;
millisecond?: number;
}
interface DateTimeFields extends DateFields, TimeFields {}
type DateField = keyof DateFields;
type TimeField = keyof TimeFields;
// Segment types for date field editing
type SegmentType = 'era' | 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'dayPeriod' | 'literal' | 'timeZoneName';
interface DateSegment {
type: SegmentType;
text: string;
value?: number;
minValue?: number;
maxValue?: number;
isPlaceholder: boolean;
placeholder: string;
isEditable: boolean;
}
// Range types for date range pickers
interface RangeValue<T> {
start: T;
end: T;
}
type DateRange = RangeValue<DateValue>;
type TimeRange = RangeValue<TimeValue>;
// Validation and formatting
interface FieldOptions {
year?: 'numeric' | '2-digit';
month?: 'numeric' | '2-digit' | 'narrow' | 'short' | 'long';
day?: 'numeric' | '2-digit';
hour?: 'numeric' | '2-digit';
minute?: 'numeric' | '2-digit';
second?: 'numeric' | '2-digit';
}
interface FormatterOptions {
timeZone?: string;
hideTimeZone?: boolean;
granularity?: Granularity;
maxGranularity?: 'year' | 'month' | Granularity;
hourCycle?: 12 | 24;
showEra?: boolean;
shouldForceLeadingZeros?: boolean;
}
// Validation and state types from React Stately ecosystem
type ValidationState = 'valid' | 'invalid';
interface FormValidationState {
/** Whether the input value is invalid according to validation rules */
isInvalid: boolean;
/** The current validation state @deprecated Use isInvalid instead */
validationState: ValidationState | null;
/** Validation errors */
validationErrors: string[];
/** Commit validation state changes */
commitValidation(): void;
/** Update validation state */
updateValidation(result: ValidationResult): void;
/** Display validation state for UI rendering */
displayValidation: {
isInvalid: boolean;
validationErrors: string[];
};
}
interface OverlayTriggerState {
/** Whether the overlay is currently open */
isOpen: boolean;
/** Sets whether the overlay is open */
setOpen(isOpen: boolean): void;
/** Opens the overlay */
open(): void;
/** Closes the overlay */
close(): void;
/** Toggles the overlay open/closed state */
toggle(): void;
}
interface ValidationResult {
/** Whether the value is invalid */
isInvalid: boolean;
/** Array of validation error messages */
validationErrors: string[];
/** Detailed validation state for different error types */
validationDetails: ValidityState;
}