Date Picker
A date selection component built on Calendar and Popover.
When to use
Compose Calendar inside a Popover when a date or a range is one field in a form or a filter bar and the month should stay out of the way until it is asked for. Reach for plain Calendar when the month must be permanently visible, and for a date entered as text or a value that includes a time use Input with a zod schema rather than stretching this pattern.
Platform notes
This is a recipe, not a registry entry: Calendar, Popover and Button come from shared/ui, while the composed field lives in the slice that owns it and is exported through that slice's index.ts. Everything the trigger renders is user-facing, so the placeholder, the range separator and the date-fns pattern all come from i18next with the matching date-fns locale; the demo's hardcoded placeholder and LLL d, y pattern do not survive review. Selection is controlled in the demo while the popover's open state is not, and in mode="range" the value passes through a state where from is set and to is still undefined, so guard on both before you submit and do not take over open just to close on the first click; the trigger is a Button, not a labelled input, so give it its own accessible name instead of relying on the formatted date. Keep PopoverContent at w-auto p-0: the default w-72 is too narrow for numberOfMonths={2}, the calendar drops its own background inside data-slot=popover-content, and the popover border carries the elevation because its shadow-md paints nothing, so verify the two-month layout below md where the months stack into a column.
Install
Compose it from calendar and popover, both of which are registry items:
npx shadcn@latest add calendar popoverThere is no shadcn add date-picker: it is a pattern you assemble, not a registry item.
Code
"use client";
import { format } from "date-fns";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { CalendarIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import type { DateRange } from "react-day-picker";
export function DatePickerDemo() {
const [date, setDate] = useState<DateRange | undefined>({
from: new Date(2026, 0, 12),
to: new Date(2026, 0, 18),
});
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className={cn(
"w-[260px] justify-start text-left font-normal",
!date && "text-muted-foreground",
)}
>
<CalendarIcon />
{date?.from ? (
date.to ? (
<>
{format(date.from, "LLL d")} – {format(date.to, "LLL d, y")}
</>
) : (
format(date.from, "LLL d, y")
)
) : (
<span>Pick a date range</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar mode="range" selected={date} onSelect={setDate} numberOfMonths={2} />
</PopoverContent>
</Popover>
);
}