Date Picker
A date picker component with range and presets.
"use client";
import { useState } from "react";
import { LuCalendar } from "react-icons/lu";
import dayjs from "dayjs";
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] = useState<Date>();
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
css={{
w: "280px",
justifyContent: "start",
textAlign: "left",
fontWeight: "normal",
color: !date ? "muted.fg" : "fg",
}}
>
<LuCalendar />
{date ? dayjs(date).format("YYYY-MM-DD") : <span>Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent css={{ w: "auto", 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 { useState } from "react";
import { LuCalendar } from "react-icons/lu";
import dayjs from "dayjs";
import { css } from "styled-system/css";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Popover } from "@/components/ui/popover";
export default function DatePickerDemo() {
const [date, setDate] = useState<Date>();
return (
<Popover.Root>
<Popover.Trigger asChild>
<Button
variant="outline"
className={css({
w: "280px",
justifyContent: "start",
textAlign: "left",
fontWeight: "normal",
color: !date ? "muted.fg" : "fg",
})}
>
<LuCalendar />
{date ? dayjs(date).format("YYYY-MM-DD") : <span>Pick a date</span>}
</Button>
</Popover.Trigger>
<Popover.Content className={css({ w: "auto", p: "0" })}>
<Calendar mode="single" selected={date} onSelect={setDate} autoFocus />
</Popover.Content>
</Popover.Root>
);
}
See the React DayPicker documentation for more information.
Examples
Date Range Picker
"use client";
import { useState } from "react";
import { DateRange } from "react-day-picker";
import { LuCalendar } from "react-icons/lu";
import dayjs from "dayjs";
import { styled } from "styled-system/jsx";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
export default function DatePickerWithRange() {
const [date, setDate] = useState<DateRange | undefined>({
from: new Date(2022, 0, 20),
to: dayjs("2022-01-20").add(7, "day").toDate(),
});
return (
<styled.div css={{ display: "grid", gap: "2" }}>
<Popover>
<PopoverTrigger asChild>
<Button
id="date"
variant="outline"
css={{
w: "280px",
justifyContent: "flex-start",
textAlign: "left",
fontWeight: "normal",
color: !date ? "muted.fg" : "fg",
}}
>
<LuCalendar />
{date?.from ? (
date.to ? (
<>
{dayjs(date.from).format("DD MMM YYYY")} - {dayjs(date.to).format("DD MMM YYYY")}
</>
) : (
dayjs(date.from).format("DD MMM YYYY")
)
) : (
<span>Pick a date</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent css={{ w: "auto", p: "0" }} align="start">
<Calendar
autoFocus
mode="range"
defaultMonth={date?.from}
selected={date}
onSelect={setDate}
numberOfMonths={2}
/>
</PopoverContent>
</Popover>
</styled.div>
);
}
With Presets
"use client";
import * as React from "react";
import { LuCalendar } from "react-icons/lu";
import dayjs from "dayjs";
import { styled } from "styled-system/jsx";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
export default function DatePickerWithPresets() {
const [date, setDate] = React.useState<Date>();
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
css={{
w: "280px",
justifyContent: "flex-start",
textAlign: "left",
fontWeight: "normal",
color: !date ? "muted.fg" : "fg",
}}
>
<LuCalendar />
{date ? dayjs(date).format("YYYY-MM-DD") : <span>Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent css={{ display: "flex", w: "auto", flexDir: "column", gap: "2", p: "2" }}>
<Select onValueChange={(value) => setDate(dayjs().add(parseInt(value), "day").toDate())}>
<SelectTrigger>
<SelectValue placeholder="Select" />
</SelectTrigger>
<SelectContent>
<SelectItem value="0">Today</SelectItem>
<SelectItem value="1">Tomorrow</SelectItem>
<SelectItem value="3">In 3 days</SelectItem>
<SelectItem value="7">In a week</SelectItem>
</SelectContent>
</Select>
<styled.div css={{ rounded: "md", borderWidth: "1px" }}>
<Calendar mode="single" selected={date} onSelect={setDate} />
</styled.div>
</PopoverContent>
</Popover>
);
}
Form
"use client";
import { Controller, useForm } from "react-hook-form";
import { LuCalendar } from "react-icons/lu";
import { zodResolver } from "@hookform/resolvers/zod";
import dayjs from "dayjs";
import { css } from "styled-system/css";
import { styled } from "styled-system/jsx";
import { z } from "zod";
import { toast } from "@/hooks/use-toast";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Field, FieldDescription, FieldError, FieldLabel } from "@/components/ui/field";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
const formSchema = z.object({
dob: z.date({
required_error: "A date of birth is required.",
}),
});
type FormSchema = z.infer<typeof formSchema>;
export default function DatePickerForm() {
const form = useForm<FormSchema>({
resolver: zodResolver(formSchema),
});
const onSubmit = form.handleSubmit((data) => {
toast({
title: "You submitted the following values:",
description: (
<styled.pre
css={{ mt: "2", w: "340px", rounded: "md", bg: "slate.950", p: "4", borderWidth: "1px" }}
>
<styled.code css={{ color: "white" }}>{JSON.stringify(data, null, 2)}</styled.code>
</styled.pre>
),
});
});
return (
<styled.form onSubmit={onSubmit} css={{ spaceY: "8" }}>
<Controller
control={form.control}
name="dob"
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel>Date of birth</FieldLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
css={{
w: "240px",
pl: "3",
textAlign: "left",
fontWeight: "normal",
color: !field.value ? "muted.fg" : undefined,
}}
>
{field.value ? dayjs(field.value).format("YYYY-MM-DD") : <span>Pick a date</span>}
<LuCalendar className={css({ ml: "auto", h: "4", w: "4", opacity: "0.5" })} />
</Button>
</PopoverTrigger>
<PopoverContent css={{ w: "auto", p: "0" }} align="start">
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
disabled={(date) => date > new Date() || date < new Date("1900-01-01")}
autoFocus
/>
</PopoverContent>
</Popover>
<FieldDescription>Your date of birth is used to calculate your age.</FieldDescription>
<FieldError>{fieldState.error?.message}</FieldError>
</Field>
)}
/>
<Button type="submit">Submit</Button>
</styled.form>
);
}