suiss/ui · Pattern
Date Picker
A date field that opens a calendar popover on desktop and wheel pickers in a drawer on mobile.
Date Picker is an open-source pattern from suiss/ui for React, React Native, SwiftUI and Jetpack Compose. The @esuiss/ui CLI copies its source into your project, so you own the code.
DatePicker is a single component that renders a field-style trigger showing the selected date, formatted with Intl.DateTimeFormat in the medium date style, or the placeholder. On desktop the trigger opens a Popover containing a single-selection Calendar; picking a day selects it and closes the popover.
Below the 48rem breakpoint the trigger opens a bottom Drawer with a WheelPicker of day, month and year columns, ordered to match the locale's short date format. Turning a wheel updates the value immediately, and the day is clamped to the length of the chosen month. Use it for form fields such as delivery dates or dates of birth.
Installation
CLI
Works in any project with a components.json. See Installation for the one-line registry setup. Or use the suiss/ui CLI:
Manual
- Install the dependencies and add the
cnhelper:
-
Add the pieces it builds on: Calendar, Drawer, Popover,
use-mobile, Wheel Picker. -
Copy the source into
components/patterns/date-picker.tsx:
- Update the import paths to match your project.
Usage
Use value and onValueChange for a controlled field, or defaultValue for an uncontrolled one; the value is a Date or null. locale drives the trigger format, calendar captions, weekday names, the first day of the week, day labels and the wheel order. fromYear and toYear bound both the calendar's months and the year wheel. Pass id, aria-label and aria-describedby to connect the field to a label and help text.
Examples
With value
Starts with a defaultValue, and the calendar opens on that month.
Locale
Formats the trigger and calendar in Turkish with locale="tr-TR".
Year range
Limits selection to 1940 through 2010 for a date of birth.
With title
Shows a visible Delivery date title in the mobile drawer.
With close button
Adds an icon close button to the mobile drawer.
With close label
Adds a text close button labelled Done to the mobile drawer.
Disabled
Disables the trigger so the picker cannot open.
Invalid
Marks the field invalid with aria-invalid.
Controlled
Owns the value in parent state and shows the picked date below the field.
Floating
Insets the mobile drawer from the screen edges with floating.
API Reference
DatePicker
The field trigger with its desktop calendar popover and mobile wheel drawer.
| Prop | Type | Default | Description |
|---|---|---|---|
value | Date | null | - | The selected date when controlled; null for no date. |
defaultValue | Date | null | - | The initial date when uncontrolled. |
onValueChange | (value: Date | null) => void | - | Called with the new date when the user picks a day or turns a wheel. |
placeholder | string | "Pick a date" | Text shown in the trigger when no date is selected. |
locale | string | - | BCP 47 locale used for formatting, week start, labels and wheel order; defaults to the runtime locale. |
fromYear | number | new Date().getFullYear() - 100 | The earliest selectable year. |
toYear | number | new Date().getFullYear() + 10 | The latest selectable year. |
title | string | - | Visible title of the mobile drawer; without it the drawer title is visually hidden. |
disabled | boolean | - | Disables the trigger. |
floating | boolean | - | Insets the mobile drawer from the screen edges with fully rounded corners. |
showCloseButton | boolean | false | Shows a close button in the mobile drawer. |
closeLabel | string | - | Replaces the drawer's close icon with this text; requires showCloseButton. |
id | string | - | The id of the trigger button, for use with a label. |
className | string | - | Classes applied to the trigger button. |
aria-invalid | boolean | - | Marks the field invalid and applies the destructive border and ring. |
aria-label | string | - | Accessible name of the trigger; also used as the hidden drawer title when title is absent. |
aria-describedby | string | - | IDs of elements that describe the field, such as help or error text. |
Accessibility
- The trigger is a
button; give it a name witharia-labelor alabellinked throughid. - On desktop the calendar is a
gridnamed by its month caption, and each day button is named with the full localized date, prefixed with today's relative name for the current day. Escapecloses the calendar and returns focus to the trigger.- On mobile each wheel column is labelled Day, Month or Year, and the drawer always has a title, visually hidden when
titleis not set.
Notes
The trigger carries data-slot="date-picker-trigger" and data-placeholder while empty; the popup carries data-slot="date-picker-content" and the wheels data-slot="date-picker-wheels". When no value is set, the mobile wheels start at today's date. The open state is internal and cannot be controlled.

DatePicker renders a field-style DrawerTrigger with a calendar icon and the selected date formatted with Intl.DateTimeFormat in the medium date style. The trigger sizes itself to its content rather than filling the width; pass className to change that. Pressing it opens a Drawer with three WheelPicker columns.
The column order follows the locale's short date format, falling back to month, day, year. Month names come from the locale, and the day column shrinks or grows with the chosen month and year, clamping the day when needed.
Installation
The CLI also adds what it builds on: Drawer, Icon, Text, Wheel Picker.
Manual
- Install the dependencies:
-
Add the pieces it builds on: Drawer, Icon, Text, Wheel Picker.
-
Copy the source into
components/patterns/date-picker.tsx:
- Update the import paths to match your project.
- Install the dependencies:
-
Add the pieces it builds on: Drawer, Icon, Text, Wheel Picker.
-
Copy the source into
components/patterns/date-picker.tsx:
- Update the import paths to match your project.
- Install the dependencies:
-
Add the pieces it builds on: Drawer, Icon, Text, Wheel Picker.
-
Copy the source into
components/patterns/date-picker.tsx:
- Update the import paths to match your project.
-
Target iOS 17 or later.
-
Add the Swift package to your app:
- Copy the theme files every view uses:
- Copy the source and every file it uses:
- Add the files to your app target.
- Set
minSdkto 24 or higher and add the dependencies:
- Copy the theme files every view uses:
- Copy the source and every file it uses:
- Update the
com.esuiss.uipackage names to match your project.
Usage
Use it uncontrolled with defaultValue, or controlled with value and onValueChange. The value is a Date or null; onValueChange fires on every wheel change, so there is no separate confirm step.
When no value is set the wheels start at today and the field shows placeholder. Limit the year column with fromYear and toYear.
Examples
With value
Starts with a preset date via defaultValue.

Locale
Uses tr-TR, which reorders the wheels and translates month names.

Year range
A date-of-birth field limited to 1940 through 2010.

With title
Adds a heading to the drawer with title.

With close button
Shows a close button in the drawer.

With close label
Labels the close button Done with closeLabel.

Disabled
A disabled field that cannot open the drawer.

Invalid
Shows the error border and tint with invalid.

Controlled
Holds the date in state and prints the selection below.

Floating
Detaches the drawer from the screen edges.

API Reference
DatePicker
| Prop | Type | Default | Description |
|---|---|---|---|
value | Date | null | - | The selected date when controlled, or null. |
defaultValue | Date | null | - | The initial date when uncontrolled. |
onValueChange | (value: Date | null) => void | - | Called with the new Date on every wheel change. |
placeholder | string | "Pick a date" | Text shown when no date is set. Defaults to Pick a date. |
locale | string | - | BCP 47 locale for formatting, month names and wheel order. |
fromYear | number | new Date().getFullYear() - 100 | First year in the year wheel. Defaults to 100 years ago. |
toYear | number | new Date().getFullYear() + 10 | Last year in the year wheel. Defaults to 10 years ahead. |
title | string | - | Heading shown at the top of the drawer. |
disabled | boolean | - | Prevents opening and dims the field. |
floating | boolean | - | Insets the drawer from the screen edges. |
showCloseButton | boolean | false | Shows a close button in the drawer. Defaults to false. |
closeLabel | string | - | Label of the close button. |
invalid | boolean | - | Applies the error border and background. |
className | string | - | Extra classes for the field, for example w-full to fill the width. |
accessibilityLabel | string | - | Label for the field, also used for the drawer when there is no title. |
accessibilityLabelledBy | string | - | Links the field to a visible label. |
Accessibility
- The wheels are labelled
Day,MonthandYear. - Without a
title, the drawer is labelled byaccessibilityLabelor the placeholder.
Notes
- The field uses
bg-secondary,border-destructivewith abg-destructivetint when invalid, andopacity-50when disabled; the tint is stronger in dark mode. - Without
floating, the drawer's top corners are square.










