A date picker that supports ranges and quick presets.
@lucide/react1'use client'
2
3import * as React from 'react'
4import { ChevronDownIcon } from 'lucide-react'
5
6import { Button } from '@/components/ui/button'
7import { Calendar } from '@/components/ui/calendar'
8import { Label } from '@/components/ui/label'
9import {
10 Popover,
11 PopoverContent,
12 PopoverTrigger,
13} from '@/components/ui/popover'
14
15export function Calendar22() {
16 const [open, setOpen] = React.useState(false)
17 const [date, setDate] = React.useState<Date | undefined>(undefined)
18
19 return (
20 <div className="flex flex-col gap-3">
21 <Label htmlFor="date" className="px-1">
22 Date of birth
23 </Label>
24 <Popover open={open} onOpenChange={setOpen}>
25 <PopoverTrigger asChild>
26 <Button
27 variant="outline"
28 id="date"
29 className="text-primary border-border h-10 w-60 justify-between font-medium hover:bg-gray-100"
30 >
31 {date ? date.toLocaleDateString() : 'Select date'}
32 <ChevronDownIcon />
33 </Button>
34 </PopoverTrigger>
35 <PopoverContent
36 className="w-auto overflow-hidden p-0"
37 align="start"
38 >
39 <Calendar
40 mode="single"
41 selected={date}
42 captionLayout="dropdown"
43 onSelect={(date) => {
44 setDate(date)
45 setOpen(false)
46 }}
47 />
48 </PopoverContent>
49 </Popover>
50 </div>
51 )
52}
53
1'use client'
2
3import * as React from 'react'
4import { ChevronDownIcon } from 'lucide-react'
5
6import { Button } from '@/components/ui/button'
7import { Calendar } from '@/components/ui/calendar'
8import { Label } from '@/components/ui/label'
9import {
10 Popover,
11 PopoverContent,
12 PopoverTrigger,
13} from '@/components/ui/popover'
14
15export function Calendar22() {
16 const [open, setOpen] = React.useState(false)
17 const [date, setDate] = React.useState<Date | undefined>(undefined)
18
19 return (
20 <div className="flex flex-col gap-3">
21 <Label htmlFor="date" className="px-1">
22 Date of birth
23 </Label>
24 <Popover open={open} onOpenChange={setOpen}>
25 <PopoverTrigger asChild>
26 <Button
27 variant="outline"
28 id="date"
29 className="text-primary border-border h-10 w-60 justify-between font-medium hover:bg-gray-100"
30 >
31 {date ? date.toLocaleDateString() : 'Select date'}
32 <ChevronDownIcon />
33 </Button>
34 </PopoverTrigger>
35 <PopoverContent
36 className="w-auto overflow-hidden p-0"
37 align="start"
38 >
39 <Calendar
40 mode="single"
41 selected={date}
42 captionLayout="dropdown"
43 onSelect={(date) => {
44 setDate(date)
45 setOpen(false)
46 }}
47 />
48 </PopoverContent>
49 </Popover>
50 </div>
51 )
52}
53
1'use client'
2
3import * as React from 'react'
4import { CalendarIcon } from 'lucide-react'
5
6import { Button } from '@/components/ui/button'
7import { Calendar } from '@/components/ui/calendar'
8import { Input } from '@/components/ui/input'
9import { Label } from '@/components/ui/label'
10import {
11 Popover,
12 PopoverContent,
13 PopoverTrigger,
14} from '@/components/ui/popover'
15
16function formatDate(date: Date | undefined) {
17 if (!date) {
18 return ''
19 }
20
21 return date.toLocaleDateString('en-US', {
22 day: '2-digit',
23 month: 'long',
24 year: 'numeric',
25 })
26}
27
28function isValidDate(date: Date | undefined) {
29 if (!date) {
30 return false
31 }
32 return !isNaN(date.getTime())
33}
34
35export function Calendar28() {
36 const [open, setOpen] = React.useState(false)
37 const [date, setDate] = React.useState<Date | undefined>(
38 new Date('2025-06-01'),
39 )
40 const [month, setMonth] = React.useState<Date | undefined>(date)
41 const [value, setValue] = React.useState(formatDate(date))
42
43 return (
44 <div className="flex flex-col gap-3">
45 <Label htmlFor="date" className="px-1">
46 Subscription Date
47 </Label>
48 <div className="relative flex gap-2">
49 <Input
50 id="date"
51 value={value}
52 placeholder="June 01, 2025"
53 className="pr-10"
54 onChange={(e) => {
55 const date = new Date(e.target.value)
56 setValue(e.target.value)
57 if (isValidDate(date)) {
58 setDate(date)
59 setMonth(date)
60 }
61 }}
62 onKeyDown={(e) => {
63 if (e.key === 'ArrowDown') {
64 e.preventDefault()
65 setOpen(true)
66 }
67 }}
68 />
69 <Popover open={open} onOpenChange={setOpen}>
70 <PopoverTrigger asChild>
71 <Button
72 id="date-picker"
73 variant="ghost"
74 className="absolute top-1/2 right-2 size-6 -translate-y-1/2"
75 >
76 <CalendarIcon className="size-3.5" />
77 <span className="sr-only">Select date</span>
78 </Button>
79 </PopoverTrigger>
80 <PopoverContent
81 className="w-auto overflow-hidden p-0"
82 align="end"
83 alignOffset={-8}
84 sideOffset={10}
85 >
86 <Calendar
87 mode="single"
88 selected={date}
89 captionLayout="dropdown"
90 month={month}
91 onMonthChange={setMonth}
92 onSelect={(date) => {
93 setDate(date)
94 setValue(formatDate(date))
95 setOpen(false)
96 }}
97 />
98 </PopoverContent>
99 </Popover>
100 </div>
101 </div>
102 )
103}
104
1'use client'
2
3import * as React from 'react'
4import { ChevronDownIcon } from 'lucide-react'
5
6import { Button } from '@/components/ui/button'
7import { Calendar } from '@/components/ui/calendar'
8import { Input } from '@/components/ui/input'
9import { Label } from '@/components/ui/label'
10import {
11 Popover,
12 PopoverContent,
13 PopoverTrigger,
14} from '@/components/ui/popover'
15
16export function Calendar24() {
17 const [open, setOpen] = React.useState(false)
18 const [date, setDate] = React.useState<Date | undefined>(undefined)
19
20 return (
21 <div className="flex gap-4">
22 <div className="flex flex-col gap-3">
23 <Label htmlFor="date-picker" className="px-1">
24 Date
25 </Label>
26 <Popover open={open} onOpenChange={setOpen}>
27 <PopoverTrigger asChild>
28 <Button
29 variant="outline"
30 id="date-picker"
31 className="text-primary border-border h-10 w-32 justify-between font-medium hover:bg-gray-100"
32 >
33 {date ? date.toLocaleDateString() : 'Select date'}
34 <ChevronDownIcon />
35 </Button>
36 </PopoverTrigger>
37 <PopoverContent
38 className="w-auto overflow-hidden p-0"
39 align="start"
40 >
41 <Calendar
42 mode="single"
43 selected={date}
44 captionLayout="dropdown"
45 onSelect={(date) => {
46 setDate(date)
47 setOpen(false)
48 }}
49 />
50 </PopoverContent>
51 </Popover>
52 </div>
53 <div className="flex flex-col gap-3">
54 <Label htmlFor="time-picker" className="px-1">
55 Time
56 </Label>
57 <Input
58 type="time"
59 id="time-picker"
60 step="1"
61 defaultValue="05:15:10"
62 className="appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
63 />
64 </div>
65 </div>
66 )
67}
68
This component leverages the chrono-node
library to interpret natural language dates.
1'use client'
2
3import * as React from 'react'
4import { parseDate } from 'chrono-node'
5import { CalendarIcon } from 'lucide-react'
6
7import { Button } from '@/components/ui/button'
8import { Calendar } from '@/components/ui/calendar'
9import { Input } from '@/components/ui/input'
10import { Label } from '@/components/ui/label'
11import {
12 Popover,
13 PopoverContent,
14 PopoverTrigger,
15} from '@/components/ui/popover'
16
17function formatDate(date: Date | undefined) {
18 if (!date) {
19 return ''
20 }
21
22 return date.toLocaleDateString('en-US', {
23 day: '2-digit',
24 month: 'long',
25 year: 'numeric',
26 })
27}
28
29export function Calendar29() {
30 const [open, setOpen] = React.useState(false)
31 const [value, setValue] = React.useState('In 2 days')
32 const [date, setDate] = React.useState<Date | undefined>(
33 parseDate(value) || undefined,
34 )
35 const [month, setMonth] = React.useState<Date | undefined>(date)
36
37 return (
38 <div className="flex flex-col gap-3">
39 <Label htmlFor="date" className="px-1">
40 Schedule Date
41 </Label>
42 <div className="relative flex gap-2">
43 <Input
44 id="date"
45 value={value}
46 placeholder="Tomorrow or next week"
47 className="pr-10"
48 onChange={(e) => {
49 setValue(e.target.value)
50 const date = parseDate(e.target.value)
51 if (date) {
52 setDate(date)
53 setMonth(date)
54 }
55 }}
56 onKeyDown={(e) => {
57 if (e.key === 'ArrowDown') {
58 e.preventDefault()
59 setOpen(true)
60 }
61 }}
62 />
63 <Popover open={open} onOpenChange={setOpen}>
64 <PopoverTrigger asChild>
65 <Button
66 id="date-picker"
67 variant="ghost"
68 className="absolute top-1/2 right-2 size-6 -translate-y-1/2"
69 >
70 <CalendarIcon className="size-3.5" />
71 <span className="sr-only">Select date</span>
72 </Button>
73 </PopoverTrigger>
74 <PopoverContent
75 className="w-auto overflow-hidden p-0"
76 align="end"
77 >
78 <Calendar
79 mode="single"
80 selected={date}
81 captionLayout="dropdown"
82 month={month}
83 onMonthChange={setMonth}
84 onSelect={(date) => {
85 setDate(date)
86 setValue(formatDate(date))
87 setOpen(false)
88 }}
89 />
90 </PopoverContent>
91 </Popover>
92 </div>
93 <div className="text-muted-foreground px-1 text-sm">
94 Your post will be published on{' '}
95 <span className="font-medium">{formatDate(date)}</span>.
96 </div>
97 </div>
98 )
99}
100
1'use client'
2
3import { format } from 'date-fns'
4import { CalendarIcon } from 'lucide-react'
5import { useForm } from 'react-hook-form'
6import { z } from 'zod'
7
8import { toast } from '@/components/ui/use-toast'
9import { cn } from '@/lib/utils'
10import { Button } from '@/components/ui/button'
11import { Calendar } from '@/components/ui/calendar'
12import {
13 Form,
14 FormDescription,
15 FormField,
16 FormItem,
17 FormLabel,
18 FormMessage,
19} from '@/components/ui/form'
20import {
21 Popover,
22 PopoverContent,
23 PopoverTrigger,
24} from '@/components/ui/popover'
25import { zodResolver } from '@hookform/resolvers/zod'
26
27const FormSchema = z.object({
28 dob: z.date({
29 required_error: 'A date of birth is required.',
30 }),
31})
32
33export function DatePickerForm() {
34 const form = useForm<z.infer<typeof FormSchema>>({
35 resolver: zodResolver(FormSchema),
36 })
37
38 function onSubmit(data: z.infer<typeof FormSchema>) {
39 toast({
40 title: 'You submitted the following values',
41 description: (
42 <pre className="mt-2 w-[320px] rounded-xl bg-gray-400 p-4">
43 <code className="text-white">
44 {JSON.stringify(
45 {
46 dob: format(data.dob, 'PPP'),
47 },
48 null,
49 2,
50 )}
51 </code>
52 </pre>
53 ),
54 })
55 }
56
57 return (
58 <Form {...form}>
59 <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
60 <FormField
61 control={form.control}
62 name="dob"
63 render={({ field }) => (
64 <FormItem className="flex flex-col">
65 <FormLabel>Date of birth</FormLabel>
66 <Popover>
67 <PopoverTrigger asChild>
68 <Button
69 variant={'outline'}
70 className={cn(
71 'text-primary border-border h-10 w-80 justify-between font-medium hover:bg-gray-100',
72 !field.value &&
73 'text-muted-foreground',
74 )}
75 >
76 {field.value ? (
77 format(field.value, 'PPP')
78 ) : (
79 <span>Pick a date</span>
80 )}
81 <CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
82 </Button>
83 </PopoverTrigger>
84 <PopoverContent
85 className="w-auto p-0"
86 align="start"
87 >
88 <Calendar
89 mode="single"
90 selected={field.value}
91 onSelect={field.onChange}
92 disabled={(date) =>
93 date > new Date() ||
94 date < new Date('1900-01-01')
95 }
96 captionLayout="dropdown"
97 />
98 </PopoverContent>
99 </Popover>
100 <FormDescription>
101 Your date of birth is used to calculate your
102 age.
103 </FormDescription>
104 <FormMessage />
105 </FormItem>
106 )}
107 />
108 <Button type="submit">Submit</Button>
109 </form>
110 </Form>
111 )
112}
113