"use client";
import { format } from "date-fns";import { ChevronDownIcon } from "lucide-react";import * as React from "react";
import { Button } from "@/components/ui/button";import { Calendar } from "@/components/ui/calendar";import { Popover, PopoverContent, PopoverTrigger,} from "@/components/ui/popover";
export function DatePickerDemo() { const [date, setDate] = React.useState<Date>();
return ( <Popover> <PopoverTrigger render={ <Button variant="tertiary" data-empty={!date} className="w-[212px] justify-between text-left font-normal data-[empty=true]:text-muted-foreground" /> } > {date ? format(date, "PPP") : <span>Pick a date</span>} <ChevronDownIcon data-icon="inline-end" /> </PopoverTrigger> <PopoverContent className="w-auto p-0" align="start"> <Calendar mode="single" selected={date} onSelect={setDate} defaultMonth={date} /> </PopoverContent> </Popover> );}The Date Picker is built using a composition of the <Popover /> and the <Calendar /> components.
"use client"
import * as React from "react"import { format } from "date-fns"import { CalendarIcon } from "lucide-react"
import { Button } from "@/components/ui/button"import { Calendar } from "@/components/ui/calendar"import { Popover, PopoverContent, PopoverTrigger,} from "@/components/ui/popover"
export function DatePickerDemo() { const [date, setDate] = React.useState<Date>()
return ( <Popover> <PopoverTrigger render={ <Button variant="outline" data-empty={!date} className="justify-start text-left font-normal data-[empty=true]:text-muted-foreground" /> } > <CalendarIcon /> {date ? format(date, "PPP") : <span>Pick a date</span>} </PopoverTrigger> <PopoverContent className="w-auto p-0"> <Calendar mode="single" selected={date} onSelect={setDate} /> </PopoverContent> </Popover> )}See the React DayPicker documentation for more information.
A date picker is built from Popover and Calendar (there is no DatePicker root component):
Popover├── PopoverTrigger└── PopoverContent └── CalendarA basic date picker component.
"use client";
import { format } from "date-fns";import * as React from "react";
import { Button } from "@/components/ui/button";import { Calendar } from "@/components/ui/calendar";import { Field, FieldLabel } from "@/components/ui/field";import { Popover, PopoverContent, PopoverTrigger,} from "@/components/ui/popover";
export function DatePickerSimple() { const [date, setDate] = React.useState<Date>();
return ( <Field className="mx-auto w-44"> <FieldLabel htmlFor="date-picker-simple">Date</FieldLabel> <Popover> <PopoverTrigger render={ <Button variant="tertiary" id="date-picker-simple" className="justify-start font-normal" /> } > {date ? format(date, "PPP") : <span>Pick a date</span>} </PopoverTrigger> <PopoverContent className="w-auto p-0" align="start"> <Calendar mode="single" selected={date} onSelect={setDate} defaultMonth={date} /> </PopoverContent> </Popover> </Field> );}A date picker component for selecting a range of dates.
"use client";
import { addDays, format } from "date-fns";import { CalendarIcon } from "lucide-react";import * as React from "react";import type { DateRange } from "react-day-picker";
import { Button } from "@/components/ui/button";import { Calendar } from "@/components/ui/calendar";import { Field, FieldLabel } from "@/components/ui/field";import { Popover, PopoverContent, PopoverTrigger,} from "@/components/ui/popover";
export function DatePickerWithRange() { const [date, setDate] = React.useState<DateRange | undefined>({ from: new Date(new Date().getFullYear(), 0, 20), to: addDays(new Date(new Date().getFullYear(), 0, 20), 20), });
return ( <Field className="mx-auto w-60"> <FieldLabel htmlFor="date-picker-range">Date Picker Range</FieldLabel> <Popover> <PopoverTrigger render={ <Button variant="tertiary" id="date-picker-range" className="justify-start px-2.5 font-normal" /> } > <CalendarIcon data-icon="inline-start" /> {date?.from ? ( date.to ? ( <> {format(date.from, "LLL dd, y")} -{" "} {format(date.to, "LLL dd, y")} </> ) : ( format(date.from, "LLL dd, y") ) ) : ( <span>Pick a date</span> )} </PopoverTrigger> <PopoverContent className="w-auto p-0" align="start"> <Calendar mode="range" defaultMonth={date?.from} selected={date} onSelect={setDate} numberOfMonths={2} /> </PopoverContent> </Popover> </Field> );}A date picker component for selecting a date of birth. This component includes a dropdown caption layout for date and month selection.
"use client";
import * as React from "react";
import { Button } from "@/components/ui/button";import { Calendar } from "@/components/ui/calendar";import { Field, FieldLabel } from "@/components/ui/field";import { Popover, PopoverContent, PopoverTrigger,} from "@/components/ui/popover";
export function DatePickerDob() { const [open, setOpen] = React.useState(false); const [date, setDate] = React.useState<Date | undefined>(undefined);
return ( <Field className="mx-auto w-44"> <FieldLabel htmlFor="date">Date of birth</FieldLabel> <Popover open={open} onOpenChange={setOpen}> <PopoverTrigger render={ <Button variant="tertiary" id="date" className="justify-start font-normal" /> } > {date ? date.toLocaleDateString() : "Select date"} </PopoverTrigger> <PopoverContent className="w-auto overflow-hidden p-0" align="start"> <Calendar mode="single" selected={date} defaultMonth={date} captionLayout="dropdown" onSelect={(date) => { setDate(date); setOpen(false); }} /> </PopoverContent> </Popover> </Field> );}A date picker component with an input field for selecting a date.
"use client";
import { CalendarIcon } from "lucide-react";import * as React from "react";
import { Calendar } from "@/components/ui/calendar";import { Field, FieldLabel } from "@/components/ui/field";import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput,} from "@/components/ui/input-group";import { Popover, PopoverContent, PopoverTrigger,} from "@/components/ui/popover";
function formatDate(date: Date | undefined) { if (!date) { return ""; }
return date.toLocaleDateString("en-US", { day: "2-digit", month: "long", year: "numeric", });}
function isValidDate(date: Date | undefined) { if (!date) { return false; } return !Number.isNaN(date.getTime());}
export function DatePickerInput() { const [open, setOpen] = React.useState(false); const [date, setDate] = React.useState<Date | undefined>( new Date("2025-06-01"), ); const [month, setMonth] = React.useState<Date | undefined>(date); const [value, setValue] = React.useState(formatDate(date));
return ( <Field className="mx-auto w-48"> <FieldLabel htmlFor="date-required">Subscription Date</FieldLabel> <InputGroup> <InputGroupInput id="date-required" value={value} placeholder="June 01, 2025" onChange={(e) => { const date = new Date(e.target.value); setValue(e.target.value); if (isValidDate(date)) { setDate(date); setMonth(date); } }} onKeyDown={(e) => { if (e.key === "ArrowDown") { e.preventDefault(); setOpen(true); } }} /> <InputGroupAddon align="inline-end"> <Popover open={open} onOpenChange={setOpen}> <PopoverTrigger render={ <InputGroupButton id="date-picker" variant="ghost" size="icon-xs" aria-label="Select date" /> } > <CalendarIcon /> <span className="sr-only">Select date</span> </PopoverTrigger> <PopoverContent className="w-auto overflow-hidden p-0" align="end" alignOffset={-8} sideOffset={10} > <Calendar mode="single" selected={date} month={month} onMonthChange={setMonth} onSelect={(date) => { setDate(date); setValue(formatDate(date)); setOpen(false); }} /> </PopoverContent> </Popover> </InputGroupAddon> </InputGroup> </Field> );}A date picker component with a time input field for selecting a time.
"use client";
import { format } from "date-fns";import { ChevronDownIcon } from "lucide-react";import * as React from "react";
import { Button } from "@/components/ui/button";import { Calendar } from "@/components/ui/calendar";import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";import { Input } from "@/components/ui/input";import { Popover, PopoverContent, PopoverTrigger,} from "@/components/ui/popover";
export function DatePickerTime() { const [open, setOpen] = React.useState(false); const [date, setDate] = React.useState<Date | undefined>(undefined);
return ( <FieldGroup className="mx-auto max-w-xs flex-row"> <Field> <FieldLabel htmlFor="date-picker-optional">Date</FieldLabel> <Popover open={open} onOpenChange={setOpen}> <PopoverTrigger render={ <Button variant="tertiary" id="date-picker-optional" className="w-32 justify-between font-normal" /> } > {date ? format(date, "PPP") : "Select date"} <ChevronDownIcon data-icon="inline-end" /> </PopoverTrigger> <PopoverContent className="w-auto overflow-hidden p-0" align="start"> <Calendar mode="single" selected={date} captionLayout="dropdown" defaultMonth={date} onSelect={(sel) => { setDate(sel); setOpen(false); }} /> </PopoverContent> </Popover> </Field> <Field className="w-32"> <FieldLabel htmlFor="time-picker-optional">Time</FieldLabel> <Input type="time" id="time-picker-optional" step="1" defaultValue="10:30:00" className="appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none" /> </Field> </FieldGroup> );}This component uses the chrono-node library to parse natural language dates.
"use client";
import { parseDate } from "chrono-node";import { CalendarIcon } from "lucide-react";import * as React from "react";
import { Calendar } from "@/components/ui/calendar";import { Field, FieldLabel } from "@/components/ui/field";import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput,} from "@/components/ui/input-group";import { Popover, PopoverContent, PopoverTrigger,} from "@/components/ui/popover";
function formatDate(date: Date | undefined) { if (!date) { return ""; }
return date.toLocaleDateString("en-US", { day: "2-digit", month: "long", year: "numeric", });}
export function DatePickerNaturalLanguage() { const [open, setOpen] = React.useState(false); const [value, setValue] = React.useState("In 2 days"); const [date, setDate] = React.useState<Date | undefined>( parseDate(value) || undefined, );
return ( <Field className="mx-auto max-w-xs"> <FieldLabel htmlFor="date-optional">Schedule Date</FieldLabel> <InputGroup> <InputGroupInput id="date-optional" value={value} placeholder="Tomorrow or next week" onChange={(e) => { setValue(e.target.value); const date = parseDate(e.target.value); if (date) { setDate(date); } }} onKeyDown={(e) => { if (e.key === "ArrowDown") { e.preventDefault(); setOpen(true); } }} /> <InputGroupAddon align="inline-end"> <Popover open={open} onOpenChange={setOpen}> <PopoverTrigger render={ <InputGroupButton id="date-picker" variant="ghost" size="icon-xs" aria-label="Select date" /> } > <CalendarIcon /> <span className="sr-only">Select date</span> </PopoverTrigger> <PopoverContent className="w-auto overflow-hidden p-0" align="end" sideOffset={8} > <Calendar mode="single" selected={date} captionLayout="dropdown" defaultMonth={date} onSelect={(sel) => { setDate(sel); setValue(formatDate(sel)); setOpen(false); }} /> </PopoverContent> </Popover> </InputGroupAddon> </InputGroup> <div className="px-1 text-muted-foreground text-sm"> Your post will be published on{" "} <span className="font-medium">{formatDate(date)}</span>. </div> </Field> );}"use client";
import { format } from "date-fns";import { arSA, he } from "date-fns/locale";import { ChevronDownIcon } from "lucide-react";import * as React from "react";import { arSA as arSADayPicker, he as heDayPicker,} from "react-day-picker/locale";
import { type Translations, useTranslation,} from "@/components/language-selector";import { Button } from "@/components/ui/button";import { Calendar } from "@/components/ui/calendar";import { Popover, PopoverContent, PopoverTrigger,} from "@/components/ui/popover";
const translations: Translations = { en: { dir: "ltr", values: { placeholder: "Pick a date", }, }, ar: { dir: "rtl", values: { placeholder: "اختر تاريخًا", }, }, he: { dir: "rtl", values: { placeholder: "בחר תאריך", }, },};
const dayPickerLocales = { ar: arSADayPicker, he: heDayPicker,} as const;
const dateFnsLocales = { ar: arSA, he: he,} as const;
export function DatePickerRtl() { const { dir, t, language } = useTranslation(translations, "ar"); const [date, setDate] = React.useState<Date>();
const dateFnsLocale = dir === "rtl" ? dateFnsLocales[language as keyof typeof dateFnsLocales] : undefined; const dayPickerLocale = dir === "rtl" ? dayPickerLocales[language as keyof typeof dayPickerLocales] : undefined;
return ( <Popover> <PopoverTrigger render={ <Button variant="tertiary" data-empty={!date} className="w-[212px] justify-between text-left font-normal data-[empty=true]:text-muted-foreground" dir={dir} /> } > {date ? ( format(date, "PPP", { locale: dateFnsLocale }) ) : ( <span>{t.placeholder}</span> )} <ChevronDownIcon data-icon="inline-end" /> </PopoverTrigger> <PopoverContent className="w-auto p-0" align="start" dir={dir}> <Calendar mode="single" selected={date} onSelect={setDate} defaultMonth={date} dir={dir} locale={dayPickerLocale} /> </PopoverContent> </Popover> );}To enable RTL support in the date picker, import locales from both date-fns/locale and react-day-picker/locale, then pass dir, locale to the Calendar component and dir to the PopoverContent and PopoverTrigger:
import { arSA } from "date-fns/locale"import { arSA as arSADayPicker } from "react-day-picker/locale"
<Popover> <PopoverContent className="w-auto p-0" align="start" dir="rtl"> <Calendar mode="single" selected={date} onSelect={setDate} dir="rtl" locale={arSADayPicker} /> </PopoverContent></Popover>