CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-stately--datepicker

React state management hooks for date picker components with internationalization and accessibility support.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

@react-stately/datepicker

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

Package Information

  • Package Name: @react-stately/datepicker
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @react-stately/datepicker

Core Imports

import { 
  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");

Basic Usage

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>
  );
}

Architecture

@react-stately/datepicker is built around several key components:

  • State Management Hooks: Four specialized hooks for different date/time input scenarios
  • Internationalization Support: Built on @internationalized/date for cross-calendar system support
  • Form Integration: Extends @react-stately/form for validation and error handling
  • Overlay Management: Integrates with @react-stately/overlays for popover state
  • Segment-based Editing: Individual editable segments for precise date/time input
  • Type Safety: Full TypeScript support with generic date value types

Capabilities

Date Picker State

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;
}

Date Picker State

Date Field State

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;
}

Date Field State

Date Range Picker State

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;
}

Date Range Picker State

Time Field State

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;
}

Time Field State

Props Interfaces

// 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;
}

Core Types

// 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;
}
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@react-stately/datepicker@3.15.x
Publish Source
CLI
Badge
tessl/npm-react-stately--datepicker badge