Date Picker
A date picker component with range and presets.
"use client"
import * as React from "react"
import { format } from "date-fns"
import { CalendarIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Calendar } from "@/components/ui/calendar"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
export default function DatePickerDemo() {
const [date, setDate] = React.useState<Date | undefined>(undefined)
return (
<Popover>
<PopoverTrigger
render={
<Button
variant="outline-dark"
className="w-56 justify-start gap-2 font-normal"
/>
}
>
<CalendarIcon
data-icon="inline-start"
className="shrink-0 opacity-70"
/>
<span className={cn(!date && "text-muted-foreground")}>
{date ? format(date, "PPP") : "Pick a date"}
</span>
</PopoverTrigger>
<PopoverContent align="start" className="w-auto gap-0 p-0">
<Calendar mode="single" selected={date} onSelect={setDate} autoFocus />
</PopoverContent>
</Popover>
)
}Installation
The Date Picker is built using a composition of the <Popover /> and the <Calendar /> components.
See installation instructions for the Popover and the Calendar components.
Usage
"use client"
import * as React from "react"
import { format } from "date-fns"
import { CalendarIcon } from "lucide-react"
import { cn } from "@/lib/utils"
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-dark"
className="w-56 justify-start gap-2 font-normal"
/>
}
>
<CalendarIcon
data-icon="inline-start"
className="shrink-0 opacity-70"
/>
<span className={cn(!date && "text-muted-foreground")}>
{date ? format(date, "PPP") : "Pick a date"}
</span>
</PopoverTrigger>
<PopoverContent align="start" className="w-auto gap-0 p-0">
<Calendar mode="single" selected={date} onSelect={setDate} autoFocus />
</PopoverContent>
</Popover>
)
}Composition
A date picker is composed from the Popover and Calendar components:
Popover
├── PopoverTrigger
└── PopoverContent
└── CalendarExamples
Basic
A basic date picker component.
"use client"
import * as React from "react"
import { format } from "date-fns"
import { CalendarIcon } from "lucide-react"
import { cn } from "@/lib/utils"
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 default function DatePickerBasic() {
const [date, setDate] = React.useState<Date | undefined>(undefined)
return (
<Field className="mx-auto w-56">
<FieldLabel htmlFor="date-picker-basic">Date</FieldLabel>
<Popover>
<PopoverTrigger
render={
<Button
variant="outline-dark"
id="date-picker-basic"
className="w-full justify-start gap-2 font-normal"
/>
}
>
<CalendarIcon
data-icon="inline-start"
className="shrink-0 opacity-70"
/>
<span className={cn(!date && "text-muted-foreground")}>
{date ? format(date, "PPP") : "Pick a date"}
</span>
</PopoverTrigger>
<PopoverContent align="start" className="w-auto gap-0 p-0">
<Calendar mode="single" selected={date} onSelect={setDate} autoFocus />
</PopoverContent>
</Popover>
</Field>
)
}Range Picker
A date picker component for selecting a range of dates.
"use client"
import * as React from "react"
import { addDays, format } from "date-fns"
import { CalendarIcon } from "lucide-react"
import { type DateRange } from "react-day-picker"
import { cn } from "@/lib/utils"
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 default function DatePickerRange() {
const [range, setRange] = React.useState<DateRange | undefined>({
from: new Date(2026, 0, 20),
to: addDays(new Date(2026, 0, 20), 20),
})
return (
<Field className="mx-auto w-64">
<FieldLabel htmlFor="date-picker-range">Date range</FieldLabel>
<Popover>
<PopoverTrigger
render={
<Button
variant="outline-dark"
id="date-picker-range"
className={cn(
"w-full justify-start gap-2 font-normal",
!range && "text-muted-foreground"
)}
/>
}
>
<CalendarIcon
data-icon="inline-start"
className="shrink-0 opacity-70"
/>
{range?.from ? (
range.to ? (
<>
{format(range.from, "LLL dd, y")} -{" "}
{format(range.to, "LLL dd, y")}
</>
) : (
format(range.from, "LLL dd, y")
)
) : (
<span>Pick a date</span>
)}
</PopoverTrigger>
<PopoverContent className="w-auto gap-0 p-0" align="start">
<Calendar
mode="range"
defaultMonth={range?.from}
selected={range}
onSelect={setRange}
numberOfMonths={2}
autoFocus
/>
</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 { ChevronDownIcon } from "lucide-react"
import { cn } from "@/lib/utils"
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 default function DatePickerDob() {
const [open, setOpen] = React.useState(false)
const [date, setDate] = React.useState<Date | undefined>(undefined)
return (
<Field className="mx-auto w-48">
<FieldLabel htmlFor="date-picker-dob">Date of birth</FieldLabel>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger
render={
<Button
variant="outline-dark"
id="date-picker-dob"
className={cn(
"w-full justify-between font-normal",
!date && "text-muted-foreground"
)}
/>
}
>
{date ? date.toLocaleDateString() : "Select date"}
<ChevronDownIcon
data-icon="inline-end"
className="shrink-0 opacity-70"
/>
</PopoverTrigger>
<PopoverContent
className="w-auto gap-0 overflow-hidden p-0"
align="start"
>
<Calendar
mode="single"
selected={date}
defaultMonth={date}
captionLayout="dropdown"
onSelect={(date) => {
setDate(date)
setOpen(false)
}}
autoFocus
/>
</PopoverContent>
</Popover>
</Field>
)
}Input
A date picker component with an input field for selecting a date.
"use client"
import * as React from "react"
import { CalendarIcon } from "lucide-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 default function DatePickerInput() {
const [open, setOpen] = React.useState(false)
const [date, setDate] = React.useState<Date | undefined>(
new Date(2026, 5, 1)
)
const [month, setMonth] = React.useState<Date | undefined>(date)
const [value, setValue] = React.useState(formatDate(date))
return (
<Field className="mx-auto w-56">
<FieldLabel htmlFor="date-picker-input">Subscription date</FieldLabel>
<InputGroup>
<InputGroupInput
id="date-picker-input"
value={value}
placeholder="June 01, 2026"
onChange={(e) => {
const nextDate = new Date(e.target.value)
setValue(e.target.value)
if (isValidDate(nextDate)) {
setDate(nextDate)
setMonth(nextDate)
}
}}
onKeyDown={(e) => {
if (e.key === "ArrowDown") {
e.preventDefault()
setOpen(true)
}
}}
/>
<InputGroupAddon align="inline-end">
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger
render={
<InputGroupButton
variant="ghost"
size="icon-xs"
aria-label="Select date"
/>
}
>
<CalendarIcon />
<span className="sr-only">Select date</span>
</PopoverTrigger>
<PopoverContent
className="w-auto gap-0 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 * as React from "react"
import { format } from "date-fns"
import { ChevronDownIcon } from "lucide-react"
import { cn } from "@/lib/utils"
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 default 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-time-date">Date</FieldLabel>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger
render={
<Button
variant="outline-dark"
id="date-picker-time-date"
className={cn(
"w-32 justify-between font-normal",
!date && "text-muted-foreground"
)}
/>
}
>
{date ? format(date, "PPP") : "Select date"}
<ChevronDownIcon
data-icon="inline-end"
className="shrink-0 opacity-70"
/>
</PopoverTrigger>
<PopoverContent
className="w-auto gap-0 overflow-hidden p-0"
align="start"
>
<Calendar
mode="single"
selected={date}
captionLayout="dropdown"
defaultMonth={date}
onSelect={(date) => {
setDate(date)
setOpen(false)
}}
/>
</PopoverContent>
</Popover>
</Field>
<Field className="w-32">
<FieldLabel htmlFor="date-picker-time-input">Time</FieldLabel>
<Input
type="time"
id="date-picker-time-input"
step="1"
defaultValue="10:30:00"
className="appearance-none bg-background [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
/>
</Field>
</FieldGroup>
)
}Natural Language Picker
This component parses natural language dates like tomorrow or in 2 days into a selected date.
"use client"
import * as React from "react"
import { CalendarIcon } from "lucide-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",
})
}
/**
* A tiny natural-language date parser. It handles a handful of common phrases
* and falls back to the native `Date` parser. Swap it for a library like
* `chrono-node` for full natural language coverage.
*/
function parseDate(input: string): Date | undefined {
const text = input.trim().toLowerCase()
if (!text) {
return undefined
}
const today = new Date()
today.setHours(0, 0, 0, 0)
const addDays = (base: Date, days: number) => {
const next = new Date(base)
next.setDate(next.getDate() + days)
return next
}
if (text === "today") return today
if (text === "tomorrow") return addDays(today, 1)
if (text === "yesterday") return addDays(today, -1)
if (text === "next week") return addDays(today, 7)
if (text === "next month") {
const next = new Date(today)
next.setMonth(next.getMonth() + 1)
return next
}
const inDays = text.match(/^in (\d+) days?$/)
if (inDays) {
return addDays(today, Number(inDays[1]))
}
const parsed = new Date(input)
return isNaN(parsed.getTime()) ? undefined : parsed
}
export default function DatePickerNaturalLanguage() {
const [open, setOpen] = React.useState(false)
const [value, setValue] = React.useState("In 2 days")
const [date, setDate] = React.useState<Date | undefined>(
parseDate("In 2 days")
)
return (
<Field className="mx-auto max-w-xs">
<FieldLabel htmlFor="date-picker-natural-language">
Schedule date
</FieldLabel>
<InputGroup>
<InputGroupInput
id="date-picker-natural-language"
value={value}
placeholder="Tomorrow or next week"
onChange={(e) => {
setValue(e.target.value)
const nextDate = parseDate(e.target.value)
if (nextDate) {
setDate(nextDate)
}
}}
onKeyDown={(e) => {
if (e.key === "ArrowDown") {
e.preventDefault()
setOpen(true)
}
}}
/>
<InputGroupAddon align="inline-end">
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger
render={
<InputGroupButton
variant="ghost"
size="icon-xs"
aria-label="Select date"
/>
}
>
<CalendarIcon />
<span className="sr-only">Select date</span>
</PopoverTrigger>
<PopoverContent
className="w-auto gap-0 overflow-hidden p-0"
align="end"
sideOffset={8}
>
<Calendar
mode="single"
selected={date}
captionLayout="dropdown"
defaultMonth={date}
onSelect={(date) => {
setDate(date)
setValue(formatDate(date))
setOpen(false)
}}
/>
</PopoverContent>
</Popover>
</InputGroupAddon>
</InputGroup>
<div className="px-1 text-sm text-muted-foreground">
Your post will be published on{" "}
<span className="font-medium">{formatDate(date)}</span>.
</div>
</Field>
)
}See the React DayPicker documentation for more information.