Command Palette

Search for a command to run...

Github

Date Picker

A date picker component with range and presets.

"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>
	);
}

Usage

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.

Composition

A date picker is built from Popover and Calendar (there is no DatePicker root component):

Popover
├── PopoverTrigger
└── PopoverContent
    └── Calendar

Examples

Basic

A 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>
	);
}

Range Picker

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>
	);
}

Date of Birth

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>
	);
}

Input

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 !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>
	);
}

Time Picker

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>
	);
}

Natural Language Picker

This component uses the chrono-node library to parse natural language dates.

Your post will be published on May 22, 2026.
"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>
	);
}

RTL

"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>