Native Date Select
A native date and time selection input built on Expo UI DatePicker and DateTimePicker, with optional form-styled input and platform-aware presentation.
Native Date Select wraps Expo UI's SwiftUI DatePicker on iOS and Jetpack Compose DateTimePicker on Android.

Installation
npx shadcn@latest add @tetra-ui/native-date-selectUsage
Modes
Set mode on the root to choose what the picker selects: date (default), time, or datetime. Always include NativeDateSelectContent.
import {
NativeDateSelect,
NativeDateSelectContent,
} from "@/components/ui/native-date-select";const [date, setDate] = useState(new Date());
<NativeDateSelect mode="date" value={date} onValueChange={setDate}>
<NativeDateSelectContent />
</NativeDateSelect>
<NativeDateSelect mode="time" value={date} onValueChange={setDate}>
<NativeDateSelectContent />
</NativeDateSelect>
<NativeDateSelect mode="datetime" value={date} onValueChange={setDate}>
<NativeDateSelectContent />
</NativeDateSelect>Use Content alone to render the picker inline. On iOS, set variant to compact, graphical, or wheel (maps to SwiftUI datePickerStyle). On Android, inline has no combined datetime UI — use the form-styled Input API for mode="datetime" (sequential date then time dialogs).
Form-styled date select
Compose NativeDateSelectTrigger, NativeDateSelectInput, and NativeDateSelectContent like Select. The Trigger opens the picker; Input is display-only; Content owns the sheet/dialog. Content has no props — the sheet title follows the Input placeholder.
Set variant on the input (or root) to choose the iOS presentation:
wheel/ default (iOS) — Trigger opens a bottom sheet with a wheel DatePickercompact(iOS) — non-pressable input; only the native compact DatePicker is interactive (Trigger optional)- Android — Trigger mounts a Material dialog (
DatePickerDialog/TimePickerDialog). Fordatetime, date then time dialogs run sequentially.variantmaps to picker/input style on the dialog.
Pass InputAddon children to Input for adornments. On iOS wheel, optionally compose NativeDateSelectSheetFooter and NativeDateSelectSheetConfirm inside Content to require an explicit confirm before committing (dismiss cancels).
import { Button } from "@/components/ui/button";
import {
NativeDateSelect,
NativeDateSelectContent,
NativeDateSelectInput,
NativeDateSelectSheetConfirm,
NativeDateSelectSheetFooter,
NativeDateSelectTrigger,
} from "@/components/ui/native-date-select";<NativeDateSelect mode="date" value={date} onValueChange={setDate}>
<NativeDateSelectTrigger asChild>
<NativeDateSelectInput placeholder="Pick a date" />
</NativeDateSelectTrigger>
<NativeDateSelectContent>
<NativeDateSelectSheetFooter>
<NativeDateSelectSheetConfirm asChild>
<Button>Confirm</Button>
</NativeDateSelectSheetConfirm>
</NativeDateSelectSheetFooter>
</NativeDateSelectContent>
</NativeDateSelect><NativeDateSelect mode="date" value={date} onValueChange={setDate}>
<NativeDateSelectInput variant="compact" placeholder="Pick a date" />
<NativeDateSelectContent />
</NativeDateSelect>Without a sheet footer, iOS wheel commits the value as it changes.
Use formatValue to customize the displayed label (defaults to Intl.DateTimeFormat by mode):
<NativeDateSelectInput
formatValue={(d) => d.toLocaleDateString()}
placeholder="Pick a date"
/>Custom trigger
Use any pressable as the Trigger child via asChild:
import { Button, ButtonText } from "@/components/ui/button";
import {
NativeDateSelect,
NativeDateSelectContent,
NativeDateSelectTrigger,
} from "@/components/ui/native-date-select";<NativeDateSelect mode="date" value={date} onValueChange={setDate}>
<NativeDateSelectTrigger asChild>
<Button size="sm" variant="secondary">
<ButtonText>{label ?? "Pick a date"}</ButtonText>
</Button>
</NativeDateSelectTrigger>
<NativeDateSelectContent />
</NativeDateSelect>Bounds
Constrain selectable dates with minimumDate and maximumDate. On iOS these map to SwiftUI DatePicker range; on Android they map to Compose selectableDates. This limits which dates can be chosen — it is not a dual start/end value.
<NativeDateSelect
mode="date"
value={date}
onValueChange={setDate}
minimumDate={new Date(2024, 0, 1)}
maximumDate={new Date(2024, 11, 31)}
>
<NativeDateSelectTrigger asChild>
<NativeDateSelectInput placeholder="Pick a date" />
</NativeDateSelectTrigger>
<NativeDateSelectContent />
</NativeDateSelect>Range selection
Expo UI pickers still select a single date. For a start–end range value, compose two pickers and cross-constrain with minimumDate / maximumDate:
const [start, setStart] = useState<Date>();
const [end, setEnd] = useState<Date>();
<NativeDateSelect
mode="date"
value={start}
onValueChange={setStart}
maximumDate={end}
>
<NativeDateSelectTrigger asChild>
<NativeDateSelectInput placeholder="Start date" />
</NativeDateSelectTrigger>
<NativeDateSelectContent />
</NativeDateSelect>
<NativeDateSelect
mode="date"
value={end}
onValueChange={setEnd}
minimumDate={start}
>
<NativeDateSelectTrigger asChild>
<NativeDateSelectInput placeholder="End date" />
</NativeDateSelectTrigger>
<NativeDateSelectContent />
</NativeDateSelect>Controlled
Pass value and onValueChange for controlled selection. Optionally control open and onOpenChange for the sheet/dialog:
const [date, setDate] = useState(new Date());
const [open, setOpen] = useState(false);
<NativeDateSelect
value={date}
onValueChange={setDate}
open={open}
onOpenChange={setOpen}
>
<NativeDateSelectTrigger asChild>
<NativeDateSelectInput placeholder="Pick a date" />
</NativeDateSelectTrigger>
<NativeDateSelectContent />
</NativeDateSelect>Disabled
<NativeDateSelect disabled value={date} onValueChange={setDate}>
<NativeDateSelectTrigger asChild>
<NativeDateSelectInput placeholder="Pick a date" />
</NativeDateSelectTrigger>
<NativeDateSelectContent />
</NativeDateSelect>API Reference
Built on Expo UI DatePicker (iOS) / DateTimePicker and Material dialogs (Android). Always include NativeDateSelectContent. Content-only renders an inline picker; with Trigger/Input, uses a form-styled input and sheet/dialog.
NativeDateSelect
Root value/open state for the date/time picker.
Prop
Type
NativeDateSelectTrigger
Opens the sheet/dialog. Extends React Native Pressable props. Must be used within NativeDateSelect.
Prop
Type
NativeDateSelectInput
Display-only form-styled input. Compose inside NativeDateSelectTrigger for wheel/Android. iOS compact embeds the native compact DatePicker and does not need a Trigger.
Prop
Type
NativeDateSelectContent
Always required. Owns presentation: inline picker, iOS wheel bottom sheet, or Android Material dialog. Hosts optional sheet footer. No props — sheet title follows the Input placeholder.
NativeDateSelectSheetFooter
Accepts BottomSheetFooter props. When present on iOS, picker changes are drafted until confirm; dismiss cancels.
NativeDateSelectSheetConfirm
Extends React Native Pressable props. Commits the draft date and closes the sheet. Must be used within NativeDateSelect.
Prop
Type
Changelog
2026-08-04 - Trigger and Content composition
Add NativeDateSelectTrigger and NativeDateSelectContent. NativeDateSelectInput is display-only; open the picker via Trigger (or use any pressable with asChild). Sheet footer moves into Content.
2026-08-05 - Mandatory Content and variant
NativeDateSelectContent is required for all usage. Renamed display to variant. Content takes no props (sheet title follows Input placeholder).