import { DatePicker, DatePickerContent, DatePickerTrigger } from "@/components/ui/date-picker";
export default function DatePickerBasic() {
return (
<DatePicker>
<DatePickerTrigger aria-label="Due date" className="w-44" />
<DatePickerContent />
</DatePicker>
);
}- Add the registryOnce per project
pnpm dlx shadcn@latest registry add @jarvis=https://ui.jarv.is/r/{name}.json - Add date-picker
pnpm dlx shadcn@latest add @jarvis/date-picker
Usage
import * as React from "react";
import { DatePicker, DatePickerContent, DatePickerTrigger } from "@/components/ui/date-picker";
export function DueDatePicker() {
const [date, setDate] = React.useState<Date | null>(null);
return (
<DatePicker value={date} onValueChange={setDate}>
<DatePickerTrigger aria-label="Due date" />
<DatePickerContent />
</DatePicker>
);
}DatePickerTrigger shows the date, or its placeholder while there's none. Give it children to
format the date yourself. DatePickerContent opens a Calendar and takes its
props.
Composition
Examples
With a label
Point a FieldLabel at the trigger's id. Without a visible label, give the trigger an
aria-label.
The release goes out at 9:00 that morning.
import { DatePicker, DatePickerContent, DatePickerTrigger } from "@/components/ui/date-picker";
import { Field, FieldDescription, FieldLabel } from "@/components/ui/field";
export default function DatePickerWithLabel() {
return (
<Field className="w-full max-w-sm">
<FieldLabel htmlFor="launch-date">Launch date</FieldLabel>
<DatePicker defaultValue={new Date(2025, 5, 12)}>
<DatePickerTrigger id="launch-date" className="w-full" />
<DatePickerContent />
</DatePicker>
<FieldDescription>The release goes out at 9:00 that morning.</FieldDescription>
</Field>
);
}Range
With mode="range", the value is a { from, to } range. numberOfMonths={2} on the content shows
two months at once.
import { DatePicker, DatePickerContent, DatePickerTrigger } from "@/components/ui/date-picker";
export default function DatePickerRange() {
return (
<DatePicker
mode="range"
defaultValue={{ from: new Date(2025, 5, 26), to: new Date(2025, 6, 4) }}
>
<DatePickerTrigger aria-label="Trip dates" placeholder="Pick dates" className="w-60" />
<DatePickerContent numberOfMonths={2} showOutsideDays={false} />
</DatePicker>
);
}Month and year dropdowns
For dates far from now, such as a birthday, captionLayout="dropdown" adds month and year menus.
Bound them with startMonth and endMonth.
import { DatePicker, DatePickerContent, DatePickerTrigger } from "@/components/ui/date-picker";
import { Field, FieldLabel } from "@/components/ui/field";
const today = new Date();
export default function DatePickerDropdowns() {
return (
<Field className="w-48">
<FieldLabel htmlFor="birthday">Date of birth</FieldLabel>
<DatePicker>
<DatePickerTrigger id="birthday" />
<DatePickerContent
captionLayout="dropdown"
defaultMonth={new Date(1990, 0)}
startMonth={new Date(1930, 0)}
endMonth={today}
/>
</DatePicker>
</Field>
);
}Disabled days
Pass disabled matchers to the content to rule out days.
We deliver on weekdays.
import { DatePicker, DatePickerContent, DatePickerTrigger } from "@/components/ui/date-picker";
import { Field, FieldDescription, FieldLabel } from "@/components/ui/field";
const today = new Date();
export default function DatePickerDisabledDays() {
return (
<Field className="w-48">
<FieldLabel htmlFor="delivery-date">Delivery date</FieldLabel>
<DatePicker>
<DatePickerTrigger id="delivery-date" />
<DatePickerContent disabled={[{ before: today }, { dayOfWeek: [0, 6] }]} />
</DatePicker>
<FieldDescription>We deliver on weekdays.</FieldDescription>
</Field>
);
}In a form
Give a Field or the picker a name, and the value submits under it: a date as YYYY-MM-DD, and
a range as YYYY-MM-DD/YYYY-MM-DD, an ISO 8601 interval. Pass form with a form's id when the
picker sits outside it. With required, the form won't submit without a date, or a range with
both its ends, which fails as valueMissing. A form reset brings back defaultValue.
import { Button } from "@/components/ui/button";
import { DatePicker, DatePickerContent, DatePickerTrigger } from "@/components/ui/date-picker";
import { Field, FieldDescription, FieldError, FieldLabel } from "@/components/ui/field";
import { Form } from "@/components/ui/form";
import { toast } from "@/components/ui/toast";
export default function DatePickerForm() {
return (
<Form
className="max-w-sm"
onFormSubmit={(values) =>
toast.add({ title: "Stay booked", description: `For ${values.stay}.` })
}
>
<Field name="stay">
<FieldLabel>Stay</FieldLabel>
<DatePicker mode="range" required>
<DatePickerTrigger placeholder="Pick your dates" className="w-full" />
<DatePickerContent numberOfMonths={2} />
</DatePicker>
<FieldDescription>Check in and check out.</FieldDescription>
<FieldError match="valueMissing">Choose the dates of your stay.</FieldError>
</Field>
<Button type="submit" variant="primary" className="self-start">
Book stay
</Button>
</Form>
);
}Sizes
size on the trigger matches the sizes of buttons, inputs and selects.
import { DatePicker, DatePickerContent, DatePickerTrigger } from "@/components/ui/date-picker";
const sizes = ["sm", "default", "lg"] as const;
export default function DatePickerSizes() {
return (
<div className="flex flex-wrap items-center gap-3">
{sizes.map((size) => (
<DatePicker key={size} defaultValue={new Date(2025, 5, 12)}>
<DatePickerTrigger size={size} aria-label={`Date, ${size}`} />
<DatePickerContent />
</DatePicker>
))}
</div>
);
}Invalid
Set invalid on the Field, and explain the problem with a FieldError. The trigger reads out
the field's FieldDescription and FieldError, and turns invalid or disabled with the field.
import { DatePicker, DatePickerContent, DatePickerTrigger } from "@/components/ui/date-picker";
import { Field, FieldError, FieldLabel } from "@/components/ui/field";
export default function DatePickerInvalid() {
return (
<Field invalid className="w-48">
<FieldLabel htmlFor="start-date">Start date</FieldLabel>
<DatePicker>
<DatePickerTrigger id="start-date" />
<DatePickerContent />
</DatePicker>
<FieldError>Choose when the contract starts.</FieldError>
</Field>
);
}Props
DatePickerContent takes DayPicker's props
other than the selection ones, plus side, sideOffset, align and alignOffset.
The tables leave out className, style and render. Parts built on Base UI take them as its styling and composition guides describe.
DatePicker
| Prop | Type | Default |
|---|---|---|
defaultOpenWhether the popover is initially open. To render a controlled popover, use the | boolean | false |
openWhether the popover is currently open. | boolean | — |
onOpenChangeEvent handler called when the popover is opened or closed. | ((open: boolean, eventDetails: Popover.Root.ChangeEventDetails) => void) | — |
onOpenChangeCompleteEvent handler called after any animations complete when the popover is opened or closed. | ((open: boolean) => void) | — |
actionsRefA ref to imperative actions.
| React.RefObject<PopoverRootActions | null> | — |
modalDetermines if the popover enters a modal state when open.
On touch devices, a When When | "trap-focus" | boolean | false |
triggerIdID of the trigger that the popover is associated with.
This is useful in conjunction with the | string | null | — |
defaultTriggerIdID of the trigger that the popover is associated with.
This is useful in conjunction with the | string | null | — |
handleA handle to associate the popover with a trigger. If specified, allows external triggers to control the popover's open state. | Popover.Handle<unknown> | — |
childrenThe content of the popover.
This can be a regular React node or a render function that receives the | PayloadChildRenderFunction<unknown> | React.ReactNode | — |
modeWhether people pick one date, or a range from a start date to an end date. | "range" | "single" | "single" |
valueThe picked date, or range for | Date | DateRange | null | — |
defaultValue | Date | DateRange | null | null |
onValueChangeCalled once a date is picked, or once a range has both its ends, and with | ((value: Date | DateRange | null) => void) | — |
localeA DayPicker locale, for the calendar and the date shown on the trigger. | Partial<DayPickerLocale> | — |
nameSubmits the value with a form under this name: a date as | string | — |
formThe | string | — |
requiredWhether a form can't be submitted without a date, or a range with both its ends. | boolean | false |
DatePickerTrigger
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native | boolean | true |
idID of the trigger. In addition to being forwarded to the rendered element,
it is also used to specify the active trigger for the popover in controlled mode (with the PopoverRoot | string | — |
handleA handle to associate the trigger with a popover. | Popover.Handle<unknown> | — |
payloadA payload to pass to the popover when it is opened. | unknown | — |
openOnHoverWhether the popover should also open when the trigger is hovered. | boolean | false |
delayHow long to wait before the popover may be opened on hover. Specified in milliseconds. Requires the | number | 300 |
closeDelayHow long to wait before closing the popover that was opened on hover. Specified in milliseconds. Requires the | number | 0 |
size | "default" | "lg" | "sm" | "default" |
placeholderShown while there's no date. | string | "Pick a date" |
DatePickerContent
| Prop | Type | Default |
|---|---|---|
sideWhich side of the anchor element to align the popup against. May automatically change to avoid collisions. | "bottom" | "inline-end" | "inline-start" | "left" | "right" | "top" | "bottom" |
sideOffsetDistance between the anchor and the popup in pixels. Also accepts a function that returns the distance to read the dimensions of the anchor and positioner elements, along with its side and alignment. The function takes a
| number | OffsetFunction | 4 |
alignHow to align the popup relative to the specified side. | "center" | "end" | "start" | "start" |
alignOffsetAdditional offset along the alignment axis in pixels. Also accepts a function that returns the offset to read the dimensions of the anchor and positioner elements, along with its side and alignment. The function takes a
| number | OffsetFunction | 0 |