Skip to main content
Gremorie

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 grid

Installation

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>

PropTypeDefaultDescription
valueDate-Controlled selected date.
onValueChange(date: Date | undefined) => void-Fires when the user picks a date in the calendar.
placeholderstring"Selecione uma data"Shown in the trigger when value is empty.
disabledbooleanfalseDisables the trigger.
classNamestring-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 trigger
  • format(value, "PP") from date-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

  1. <DatePicker> is the canonical compact pattern.
  2. When you outgrow it, assemble your own Popover + Calendar (see the snippet below) - this is what DatePicker does internally and is the default starting point for variants.
  3. For mobile, swap Popover for Drawer so 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 Popover behaviour: Esc closes the popover; focus returns to the trigger on close.
  • autoFocus on 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 disabled so the button removes itself from the tab order and dims to 50% opacity.
  • Calendar - the underlying grid primitive
  • Popover - the wrapping floating surface
  • Drawer - mobile alternative to Popover
  • Form - wire DatePicker into react-hook-form

On this page