Skip to content
Lenso UI

DatePicker

Composable date picker built on React Aria DatePicker with DateField and Calendar composition

Usage

import { DatePicker, DateField, Calendar, Label } from '@lenso/ui';
"use client";
/** Adapted from the pinned HeroUI v3.2.6 basic.json. Copyright NextUI Inc. Apache-2.0. Local RAC label and StyleX replace source Label and utility classes. */import { Calendar, DateField, DatePicker } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";
const styles = stylex.create({ field: { width: 288 } });export function Basic() {  return (    <DatePicker xstyle={styles.field} name="date">      <DatePicker.Label>Date</DatePicker.Label>      <DateField.Group fullWidth>        <DateField.Input>{(segment) => <DateField.Segment segment={segment} />}</DateField.Input>        <DateField.Suffix>          <DatePicker.Trigger>            <DatePicker.TriggerIndicator />          </DatePicker.Trigger>        </DateField.Suffix>      </DateField.Group>      <DatePicker.Popover>        <Calendar aria-label="Event date">          <Calendar.Header>            <Calendar.YearPickerTrigger>              <Calendar.YearPickerTriggerHeading />              <Calendar.YearPickerTriggerIndicator />            </Calendar.YearPickerTrigger>            <Calendar.NavButton slot="previous" />            <Calendar.NavButton slot="next" />          </Calendar.Header>          <Calendar.Grid>            <Calendar.GridHeader>              {(day) => <Calendar.HeaderCell>{day}</Calendar.HeaderCell>}            </Calendar.GridHeader>            <Calendar.GridBody>{(date) => <Calendar.Cell date={date} />}</Calendar.GridBody>          </Calendar.Grid>          <Calendar.YearPickerGrid>            <Calendar.YearPickerGridBody>              {({ year }) => <Calendar.YearPickerCell year={year} />}            </Calendar.YearPickerGridBody>          </Calendar.YearPickerGrid>        </Calendar>      </DatePicker.Popover>    </DatePicker>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Anatomy

DatePicker follows a composition-first API. Compose DateField and Calendar explicitly to control structure and styling.

import {Calendar, DateField, DatePicker, Label} from '@lenso/ui';
export default () => (  <DatePicker>    <Label />    <DateField.Group>      <DateField.Input>        {(segment) => <DateField.Segment segment={segment} />}      </DateField.Input>      <DateField.Suffix>        <DatePicker.Trigger>          <DatePicker.TriggerIndicator />        </DatePicker.Trigger>      </DateField.Suffix>    </DateField.Group>    <DatePicker.Popover>      <Calendar aria-label="Choose date">        <Calendar.Header>          <Calendar.YearPickerTrigger>            <Calendar.YearPickerTriggerHeading />            <Calendar.YearPickerTriggerIndicator />          </Calendar.YearPickerTrigger>          <Calendar.NavButton slot="previous" />          <Calendar.NavButton slot="next" />        </Calendar.Header>        <Calendar.Grid>          <Calendar.GridHeader>            {(day) => <Calendar.HeaderCell>{day}</Calendar.HeaderCell>}          </Calendar.GridHeader>          <Calendar.GridBody>{(date) => <Calendar.Cell date={date} />}</Calendar.GridBody>        </Calendar.Grid>      </Calendar>    </DatePicker.Popover>  </DatePicker>)

Examples

Disabled

"use client";/** HeroUI v3.2.6 adapted example. Copyright NextUI Inc. Apache-2.0. */export { Disabled } from "./scenarios";

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Controlled

"use client";/** HeroUI v3.2.6 adapted example. Copyright NextUI Inc. Apache-2.0. */export { Controlled } from "./scenarios";

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Validation

"use client";/** HeroUI v3.2.6 adapted example. Copyright NextUI Inc. Apache-2.0. */export { WithValidation } from "./scenarios";

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Format Options

Control how DatePicker values are displayed with props such as granularity, hourCycle, hideTimeZone, and shouldForceLeadingZeros.

"use client";/** Adapted from HeroUI v3.2.6 format-options.json. Copyright NextUI Inc. Apache-2.0. */import { DatePicker, TimeField } from "@lenso/ui";import {  getLocalTimeZone,  parseDate,  parseZonedDateTime,  type DateValue,} from "@internationalized/date";import { useMemo, useState } from "react";import * as stylex from "@stylexjs/stylex";import { PickerCalendar, PickerInput } from "./parts";import { FormatControls, formatStyles, type Granularity, type HourCycle } from "./format-controls";
export function FormatOptions() {  const [granularity, setGranularity] = useState<Granularity>("minute");  const [hourCycle, setHourCycle] = useState<HourCycle>(12);  const [hideTimeZone, setHideTimeZone] = useState(false);  const [shouldForceLeadingZeros, setShouldForceLeadingZeros] = useState(false);  const timeGranularity = granularity !== "day" ? granularity : undefined;  const defaultValue = useMemo<DateValue>(    () =>      granularity === "day"        ? parseDate("2026-02-03")        : parseZonedDateTime(`2026-02-03T08:45:00[${getLocalTimeZone()}]`),    [granularity],  );  return (    <div {...stylex.props(formatStyles.stack)}>      <DatePicker        key={granularity}        xstyle={formatStyles.field}        defaultValue={defaultValue}        granularity={granularity}        hourCycle={hourCycle}        hideTimeZone={hideTimeZone}        shouldForceLeadingZeros={shouldForceLeadingZeros}        name="date"      >        {({ state }) => (          <>            <DatePicker.Label>Date and time</DatePicker.Label>            <PickerInput />            <DatePicker.Popover xstyle={formatStyles.popover}>              <PickerCalendar />              {timeGranularity && (                <div {...stylex.props(formatStyles.timeRow)}>                  <span {...stylex.props(formatStyles.label)}>Time</span>                  <TimeField                    aria-label="Time"                    granularity={timeGranularity}                    hourCycle={hourCycle}                    hideTimeZone={hideTimeZone}                    name="time"                    shouldForceLeadingZeros={shouldForceLeadingZeros}                    value={state.timeValue}                    onChange={(value) => {                      if (value) state.setTimeValue(value);                    }}                  >                    <TimeField.Group variant="secondary">                      <TimeField.Input>                        {(segment) => <TimeField.Segment segment={segment} />}                      </TimeField.Input>                    </TimeField.Group>                  </TimeField>                </div>              )}            </DatePicker.Popover>          </>        )}      </DatePicker>      <FormatControls        {...{          granularity,          setGranularity,          hourCycle,          setHourCycle,          hideTimeZone,          setHideTimeZone,          shouldForceLeadingZeros,          setShouldForceLeadingZeros,        }}      />    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Form Example

"use client";/** HeroUI v3.2.6 adapted example. Copyright NextUI Inc. Apache-2.0. */export { FormExample } from "./scenarios";

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Custom Indicator

DatePicker.TriggerIndicator renders the default IconCalendar when no children are provided. Pass children to replace it.

"use client";/** HeroUI v3.2.6 adapted example. Copyright NextUI Inc. Apache-2.0. */export { WithCustomIndicator } from "./scenarios";

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Render Function

"use client";/** Adapted from HeroUI v3.2.6 render-function.json. Copyright NextUI Inc. Apache-2.0. RAC 1.21 native DOM render functions preserve props, refs and input semantics. */import { DateField, DatePicker } from "@lenso/ui";import { PickerCalendar, styles } from "./parts";
export function RenderFunction() {  return (    <DatePicker      xstyle={styles.field}      name="date"      render={(props) => <div {...props} data-custom="date-picker" />}    >      <DatePicker.Label render={(props) => <span {...props} data-custom="date-picker-label" />}>        Date      </DatePicker.Label>      <DateField.Group        fullWidth        render={(props) => <div {...props} data-custom="date-field-group" />}      >        <DateField.Input render={(props) => <div {...props} data-custom="date-field-input" />}>          {(segment) => (            <DateField.Segment              render={(props) => <span {...props} data-custom="date-field-segment" />}              segment={segment}            />          )}        </DateField.Input>        <DateField.Suffix>          <DatePicker.Trigger            render={(props) => <button {...props} data-custom="date-picker-trigger" />}          >            <DatePicker.TriggerIndicator />          </DatePicker.Trigger>        </DateField.Suffix>      </DateField.Group>      <DatePicker.Popover>        <PickerCalendar />      </DatePicker.Popover>    </DatePicker>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

International Calendar

By default, DatePicker displays dates using the calendar system for the user's locale. You can override this by wrapping your DatePicker with I18nProvider and setting the Unicode calendar locale extension.

The example below shows the Indian calendar system:

"use client";/** HeroUI v3.2.6 adapted example. Copyright NextUI Inc. Apache-2.0. */export { InternationalCalendar } from "./scenarios";

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Note: The onChange event always returns a date in the same calendar system as the value or defaultValue (Gregorian if no value is provided), regardless of the displayed locale. This ensures your application logic works consistently with a single calendar system while still displaying dates in the user's preferred format.

For a complete list of supported calendar systems and their identifiers, see:

Customization

Tailwind CSS

"use client";/** HeroUI v3.2.6 adapted example. Copyright NextUI Inc. Apache-2.0. */export { CustomStyles } from "./scenarios";

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Global CSS

To customize DatePicker base classes, use @layer components.

@layer components {  .date-picker {    @apply inline-flex flex-col gap-1;  }
  .date-picker__trigger {    @apply inline-flex items-center justify-between;  }
  .date-picker__trigger-indicator {    @apply text-muted;  }
  .date-picker__popover {    @apply min-w-[var(--trigger-width)] p-0;  }}

Styling Reference

HeroUI follows BEM naming for reusable customization.

CSS Classes

DatePicker uses these classes in packages/styles/components/date-picker.css:

  • .date-picker - Root wrapper.
  • .date-picker__trigger - Trigger part that opens the popover.
  • .date-picker__trigger-indicator - Default/custom indicator slot.
  • .date-picker__popover - Popover content wrapper.

Interactive States

DatePicker supports React Aria data attributes and pseudo states:

  • Open: [data-open="true"] on trigger.
  • Disabled: [data-disabled="true"] or [aria-disabled="true"] on trigger.
  • Focus visible: :focus-visible or [data-focus-visible="true"] on trigger.
  • Hover: :hover or [data-hovered="true"] on trigger.

API Reference

DatePicker

DatePicker inherits all props from React Aria DatePicker.

PropTypeDefaultDescription
valueDateValue | null-Controlled selected date value.
defaultValueDateValue | null-Default selected value in uncontrolled mode.
onChange(value: DateValue | null) => void-Called when selected date changes.
isOpenboolean-Controlled popover open state.
defaultOpenbooleanfalseInitial popover open state.
onOpenChange(isOpen: boolean) => void-Called when popover open state changes.
isDisabledbooleanfalseDisables date selection and trigger interactions.
isInvalidboolean-Marks the field as invalid for validation state.
minValueDateValue-Minimum selectable date.
maxValueDateValue-Maximum selectable date.
namestring-Name used for HTML form submission.
childrenReactNode | (values: DatePickerRenderProps) => ReactNode-Composed content or render function.
renderDOMRenderFunction<keyof React.JSX.IntrinsicElements, DatePickerRenderProps>-Overrides the default DOM element with a custom render function.

Composition Parts

ComponentDescription
DatePicker.RootRoot date picker container and state owner.
DatePicker.TriggerTrigger button, usually rendered inside DateField.Suffix.
DatePicker.TriggerIndicatorIndicator slot with default calendar icon.
DatePicker.PopoverPopover wrapper for Calendar content.
  • @internationalized/date — date types (CalendarDate, CalendarDateTime, ZonedDateTime) and utilities used by all date components
  • I18nProvider — override locale for a subtree
  • useLocale — read the current locale and layout direction