Date Picker
Composite of Popover + Calendar - the canonical ready-to-use date input distributed by the Gremorie registry.
Overview
DatePicker is not a new primitive; it's the canonical composition of Popover + Calendar that the registry exposes as a ready-to-use wrapper. Use it for compact forms where you want a date input that opens a calendar grid on click.
Single-mode by default. For ranges, use <DatePickerRange /> (separate registry item). For mobile UX, prefer Drawer + Calendar - popovers don't behave well on small screens.
DatePicker lives in @gremorie/rx-overlays because it composes a Popover (overlays) with a Calendar (forms). The dependency chain runs overlays -> forms, so the wrapper has to sit on the overlays side.
Preview
'use client';import { DatePicker } from '@gremorie/rx-overlays';import { useState } from 'react';export function DatePickerPreview() { const [date, setDate] = useState<Date | undefined>(undefined); return ( <DatePicker value={date} onValueChange={setDate} placeholder="Pick a date" /> );}Anatomy
DatePicker composite of Popover + Calendar with Gremorie defaults
├─ Popover floating-surface root
│ ├─ PopoverTrigger asChild wrapper around the outlined Button
│ │ └─ Button outlined trigger (formatted date or placeholder)
│ └─ PopoverContent floating surface (w-auto p-0)
│ └─ Calendar single-mode date gridInstallation
bash npx gremorie@latest add rx-date-picker bash pnpm dlx gremorie@latest add rx-date-picker
bash yarn dlx gremorie@latest add rx-date-picker
bash bunx --bun gremorie@latest add rx-date-picker
Usage
import { DatePicker } from "@gremorie/rx-overlays";
export function Example() {
const [date, setDate] = React.useState<Date>();
return (
<DatePicker value={date} onValueChange={setDate} placeholder="Pick a date" />
);
}The Angular edition of this component ships from source today (see the workbench for the side-by-side); its registry entry is coming next.
API
<DatePicker>
| Prop | Type | Default | Description |
|---|---|---|---|
value | Date | - | Controlled selected date. |
onValueChange | (date: Date | undefined) => void | - | Fires when the user picks a date in the calendar. |
placeholder | string | "Selecione uma data" | Shown in the trigger when value is empty. |
disabled | boolean | false | Disables the trigger. |
className | string | - | Classes on the trigger Button. Default trigger width is w-[240px]. |
This is the canonical composition. If you need different behaviour (multiple, range, custom trigger, different format), assemble your own Popover + Calendar instead - this wrapper intentionally stays narrow.
Internally:
<PopoverTrigger asChild>+<Button variant="outline">- the triggerformat(value, "PP")fromdate-fns- the formatted value ("Apr 29, 2026")<PopoverContent align="start" className="w-auto p-0">- the floating surface<Calendar mode="single" selected={value} onSelect={onValueChange} autoFocus />- the picker
Composition
<DatePicker>is the canonical compact pattern.- When you outgrow it, assemble your own
Popover+Calendar(see the snippet below) - this is whatDatePickerdoes internally and is the default starting point for variants. - For mobile, swap
PopoverforDrawerso the calendar takes the full bottom sheet instead of a tiny popover above the keyboard.
Variations
Controlled single date (default)
function ScheduledFor() {
const [date, setDate] = React.useState<Date>();
return (
<div className="grid gap-2">
<Label htmlFor="scheduled-for">Scheduled for</Label>
<DatePicker
value={date}
onValueChange={setDate}
placeholder="Pick a date"
/>
</div>
);
}Custom trigger (assemble your own)
When you need a different trigger - inline text, a tag, an icon button - skip the wrapper and compose directly. This is what DatePicker does internally.
import { format } from 'date-fns';
import { CalendarIcon } from 'lucide-react';
import { Button, Calendar } from '@gremorie/rx-forms';
import { Popover, PopoverContent, PopoverTrigger } from '@gremorie/rx-overlays';
function CustomTriggerDatePicker() {
const [date, setDate] = React.useState<Date>();
return (
<Popover>
<PopoverTrigger asChild>
<Button variant="ghost" size="sm">
<CalendarIcon />
{date ? format(date, 'PP') : 'Pick a date'}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar mode="single" selected={date} onSelect={setDate} autoFocus />
</PopoverContent>
</Popover>
);
}Date range (assemble your own)
For range pickers, build the composition with mode="range" and numberOfMonths={2}.
import type { DateRange } from 'react-day-picker';
function DateRangePicker() {
const [range, setRange] = React.useState<DateRange>();
return (
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" className="w-[280px] justify-start">
<CalendarIcon className="mr-2 size-4" />
{range?.from && range?.to
? `${format(range.from, 'PP')} - ${format(range.to, 'PP')}`
: 'Pick a range'}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="range"
selected={range}
onSelect={setRange}
numberOfMonths={2}
autoFocus
/>
</PopoverContent>
</Popover>
);
}Accessibility
- Inherits
Popoverbehaviour:Esccloses the popover; focus returns to the trigger on close. autoFocuson the Calendar: focuses the active (or today's) cell as soon as the popover opens.- Calendar keyboard: full grid navigation via arrow keys,
Home,End,PageUp,PageDown. See Calendar accessibility for the full list. - Trigger label: provide a
<Label>linked to the trigger via the field wrapper (e.g.<FormItem>) so the date picker has an accessible name. - Disabled: the trigger surfaces
disabledso the button removes itself from the tab order and dims to 50% opacity.