1
ComponentsForm Controls

Date Picker

Date Picker composes Button, Popover, and Calendar into an accessible single-date selection pattern.

Purpose

Date Picker is a composition of Button, Popover, and Calendar for selecting a single date in an application flow. Use Calendar alone when the calendar should remain visible.

Popover date picker

"use client";

import { useState } from "react";
import { CalendarIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Popover, PopoverPopup, PopoverTrigger } from "@/components/ui/popover";

export function Example() {
  const [date, setDate] = useState<Date | undefined>();
  return (
    <Popover>
      <PopoverTrigger render={<Button variant="outline" />}>
        <CalendarIcon />
        Pick date
      </PopoverTrigger>
      <PopoverPopup><Calendar mode="single" selected={date} onSelect={setDate} /></PopoverPopup>
    </Popover>
  );
}

Installation

With the Siteplane registry alias configured, add the component and import the installed source from your app.

CLI

pnpm dlx shadcn@4.16.1 add @siteplane/date-picker

Import

import { Calendar } from "@/components/ui/calendar";
import { Popover, PopoverPopup, PopoverTrigger } from "@/components/ui/popover";
import { Button } from "@/components/ui/button";

Need the machine-readable source metadata? View registry JSON.

Usage

Date Picker is a composition of Button, Popover and Calendar. There is no separate DatePicker primitive.

"use client";

import { useState } from "react";
import { CalendarIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Popover, PopoverPopup, PopoverTrigger } from "@/components/ui/popover";

export function Example() {
  const [date, setDate] = useState<Date | undefined>();
  return (
    <Popover>
      <PopoverTrigger render={<Button variant="outline" />}>
        <CalendarIcon />
        Pick date
      </PopoverTrigger>
      <PopoverPopup><Calendar mode="single" selected={date} onSelect={setDate} /></PopoverPopup>
    </Popover>
  );
}

Examples

Field wiring, trigger formatting and disabled matchers are compositions of the same three primitives.

Single date picker

The trigger shows the short-formatted date and the popover closes after a selection.

<Popover onOpenChange={setOpen} open={open}>
  <PopoverTrigger
    render={<Button aria-label="Choose date" variant="outline" />}
  >
    <CalendarIcon aria-hidden="true" />
    <span>{formatShortDate(date)}</span>
  </PopoverTrigger>
  <PopoverContent align="start" className="w-auto">
    <Calendar
      mode="single"
      onSelect={(nextDate) => {
        setDate(nextDate);
        if (nextDate) setOpen(false);
      }}
      selected={date}
    />
  </PopoverContent>
</Popover>

Field integration

FieldLabel and FieldDescription connect to the trigger via aria-labelledby and aria-describedby.

Shown on the final invoice.

<Field>
  <FieldLabel id={labelId}>Invoice date</FieldLabel>
  <Popover onOpenChange={setOpen} open={open}>
    <PopoverTrigger
      render={
        <Button
          aria-describedby={descriptionId}
          aria-labelledby={labelId}
          variant="outline"
        />
      }
    >
      <CalendarIcon aria-hidden="true" />
      <span>{formatShortDate(date)}</span>
    </PopoverTrigger>
    <PopoverContent align="start" className="w-auto">
      <Calendar mode="single" onSelect={onSelect} selected={date} />
    </PopoverContent>
  </Popover>
  <FieldDescription id={descriptionId}>
    Shown on the final invoice.
  </FieldDescription>
</Field>

With error

Pick a date before you continue.
<Field>
  <FieldLabel id={labelId}>Deadline</FieldLabel>
  <Popover onOpenChange={setOpen} open={open}>
    <PopoverTrigger
      render={
        <Button aria-invalid aria-labelledby={labelId} variant="outline" />
      }
    >
      <CalendarIcon aria-hidden="true" />
      <span>{formatShortDate(date)}</span>
    </PopoverTrigger>
    <PopoverContent align="start" className="w-auto">
      <Calendar mode="single" onSelect={onSelect} selected={date} />
    </PopoverContent>
  </Popover>
  <FieldError match>Pick a date before you continue.</FieldError>
</Field>

Formatted trigger

function formatLongDate(date: Date | undefined) {
  return date
    ? date.toLocaleDateString("en-US", {
        weekday: "short",
        day: "numeric",
        month: "long",
        year: "numeric",
      })
    : "Pick a date";
}

<PopoverTrigger
  render={<Button aria-label="Choose formatted date" variant="outline" />}
>
  <CalendarIcon aria-hidden="true" />
  <span>{formatLongDate(date)}</span>
</PopoverTrigger>

Disable past dates

Past days are blocked with the react-day-picker disabled matcher.

<Calendar
  defaultMonth={today}
  disabled={{ before: today }}
  mode="single"
  onSelect={onSelect}
  selected={date}
/>

Booking date

Booking date
<div className="grid gap-2">
  <span className="font-medium text-sm" id={labelId}>Booking date</span>
  <Popover onOpenChange={setOpen} open={open}>
    <PopoverTrigger
      render={<Button aria-labelledby={labelId} variant="outline" />}
    >
      <CalendarIcon aria-hidden="true" />
      <span>{formatShortDate(date)}</span>
    </PopoverTrigger>
    <PopoverContent align="start" className="w-auto">
      <Calendar
        defaultMonth={today}
        disabled={{ before: today }}
        mode="single"
        onSelect={onSelect}
        selected={date}
      />
    </PopoverContent>
  </Popover>
</div>

Inline calendar

Skip the popover when the calendar can stay visible. The full primitive API lives in the Calendar docs.

<Calendar
  mode="single"
  onSelect={setDate}
  selected={date}
/>

API Reference

The reference lists the source-owned exports and props that are easy to miss in visual examples. Inherited Base UI props remain available unless the wrapper narrows them.

  • Date Picker is a documented composition, not a separate primitive.
  • Keep selected date and Popover open state in one clear controlled or uncontrolled owner.
  • Pass date rules such as disabled dates to Calendar.
  • Use Field for a visible label, description, invalid state, and error text.
  • Format the trigger label with the product locale while keeping the Date value as state.

Motion

Component motion uses the shared Siteplane motion contract. See the global motion guide for provider setup, tokens and reduced-motion behavior.

  • Opening and closing uses native Popover motion; month navigation uses native Calendar motion.
  • Do not wrap the composition in another height, opacity, or scale animation.
  • Selected, disabled, and invalid states must not change the trigger dimensions.

Accessibility

  • Give the trigger visible label context, aria-label, or aria-labelledby.
  • Reflect invalid state on the trigger and render a visible FieldError.
  • Keep keyboard navigation, Escape dismissal, outside click, and focus restoration native.
  • The trigger text must communicate the selected date or a clear placeholder.

Implementation Guidance

  • Compose canonical PopoverPopup, Calendar, and Button; do not create a second Date Picker primitive.
  • Preserve native Calendar and Popover focus, dismissal, and motion.
  • Use Field for form integration.

On This Page