Shadcn Date Picker for React and Tailwind
Input with a built-in calendar.
Installation
bunx --bun shadcn@latest add https://kit.dev/r/date-picker.jsonpnpm dlx shadcn@latest add https://kit.dev/r/date-picker.jsonnpx shadcn@latest add https://kit.dev/r/date-picker.jsonyarn shadcn@latest add https://kit.dev/r/date-picker.json<Step>This component depends on Button, Calendar, Input, and Input Group. Install them first if you haven't already.</Step>
Install the following dependencies:
bun add @ark-ui/react lucide-reactpnpm add @ark-ui/react lucide-reactnpm install @ark-ui/react lucide-reactyarn add @ark-ui/react lucide-reactCopy and paste the following code into your project.
"use client";
import {
DatePicker as ArkDatePicker,
useDatePickerContext,
} from "@ark-ui/react/date-picker";
import { Portal } from "@ark-ui/react/portal";
import { CalendarIcon, ClockIcon } from "lucide-react";
import type React from "react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import {
Calendar,
CalendarPresetTrigger,
} from "@/components/ui/calendar";
import type { Input, InputProps } from "@/components/ui/input";
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from "@/components/ui/input-group";
export const useDatePicker = useDatePickerContext;
export const DatePicker = (props: React.ComponentProps<typeof Calendar>) => {
const { positioning = { placement: "top" }, ...rest } = props;
return (
<Calendar
data-slot="date-picker"
inline={false}
positioning={positioning}
{...rest}
/>
);
};
export const DatePickerTrigger = (
props: React.ComponentProps<typeof ArkDatePicker.Trigger>
) => {
const { className, children, ...rest } = props;
return (
<ArkDatePicker.Control data-slot="date-picker-control">
<ArkDatePicker.Trigger
className={cn(
"justify-start",
"text-left data-placeholder-shown:[&>span]:text-muted-foreground",
"active:scale-100",
"[&_svg:not([class*='text-'])]:opacity-64",
className
)}
data-slot="date-picker-trigger"
{...rest}
>
{children}
</ArkDatePicker.Trigger>
</ArkDatePicker.Control>
);
};
interface DatePickerInputProps
extends Omit<React.ComponentProps<typeof ArkDatePicker.Input>, "size">,
InputProps {}
export const DatePickerInput = (props: DatePickerInputProps) => {
const { size, className, ...rest } = props;
return (
<ArkDatePicker.Control data-slot="date-picker-control">
<InputGroup size={size}>
<ArkDatePicker.Input asChild data-slot="date-picker-input" {...rest}>
<InputGroupInput />
</ArkDatePicker.Input>
<InputGroupAddon align="inline-end">
<InputGroupButton
asChild
data-slot="input-group-button"
size="icon-xs"
variant="ghost"
>
<ArkDatePicker.Trigger asChild data-slot="date-picker-trigger">
<Button size="icon-md" variant="ghost">
<CalendarIcon aria-hidden className="text-muted-foreground" />
</Button>
</ArkDatePicker.Trigger>
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
</ArkDatePicker.Control>
);
};
export const DatePickerTimer = (props: React.ComponentProps<typeof Input>) => {
const { id, value, defaultValue, className, ...rest } = props;
return (
<InputGroup {...rest}>
<InputGroupAddon>
<ClockIcon />
</InputGroupAddon>
<InputGroupInput
className={cn(
"[&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none",
className
)}
defaultValue={defaultValue}
id={id}
step="1"
type="time"
value={value}
/>
</InputGroup>
);
};
export const DatePickerContent = (
props: React.ComponentProps<typeof ArkDatePicker.Content>
) => {
const { className, ...rest } = props;
return (
<Portal>
<ArkDatePicker.Positioner data-slot="date-picker-positioner">
<ArkDatePicker.Content
className={cn(
"[--cell-size:--spacing(8)]",
"z-[calc(50+var(--layer-index,0))]",
"w-fit min-w-72",
"p-3",
"bg-popover",
"text-popover-foreground",
"rounded-xl border shadow-lg/5",
"outline-none",
"origin-(--transform-origin)",
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"data-[state=closed]:animate-out data-[state=open]:animate-in",
"data-[state=closed]:zoom-out-[98%] data-[state=open]:zoom-in-[98%]",
"motion-reduce:animate-none!",
className
)}
data-slot="date-picker-content"
{...rest}
/>
</ArkDatePicker.Positioner>
</Portal>
);
};
export const DatePickerValue = (
props: React.ComponentProps<typeof ArkDatePicker.ValueText>
) => {
const { className, ...rest } = props;
return (
<ArkDatePicker.ValueText
className={cn("font-medium text-sm", className)}
data-slot="date-picker-value"
{...rest}
/>
);
};
export const DatePickerPresetTrigger = (
props: React.ComponentProps<typeof ArkDatePicker.PresetTrigger>
) => (
<CalendarPresetTrigger data-slot="date-picker-preset-trigger" {...props} />
);Update the import paths to match your project setup.
Anatomy
DatePicker
├── DatePickerTrigger
└── DatePickerContent
── (Calendar components)Usage
import {
DatePicker,
DatePickerCalendar,
DatePickerClearTrigger,
DatePickerContent,
DatePickerControl,
DatePickerInput,
DatePickerLabel,
DatePickerPositioner,
DatePickerTrigger,
} from "@/components/ui/date-picker";<DatePicker selectionMode="single">
<DatePickerLabel>Pick a date</DatePickerLabel>
<DatePickerControl>
<DatePickerInput index={0} placeholder="Select date" />
<DatePickerTrigger />
<DatePickerClearTrigger asChild>
<Button size="sm" variant="outline">Clear</Button>
</DatePickerClearTrigger>
</DatePickerControl>
<DatePickerPositioner>
<DatePickerContent>
<DatePickerCalendar />
</DatePickerContent>
</DatePickerPositioner>
</DatePicker>Examples
Input
Use DatePickerInput for a text input with calendar icon that opens the date picker.
Range
Select a date range with selectionMode="range".
Time
Use DatePickerTimer for time selection.
With Presets
Add quick single-date presets in a side panel.
Custom format
Format the selected value with Intl.DateTimeFormat.
API Reference
DatePicker
Root element. Extends Calendar.
| Prop | Type | Default |
|---|---|---|
value | DateValue[] | - |
defaultValue | DateValue[] | - |
onValueChange | (details: ValueChangeDetails) => void | - |
selectionMode | "single" | "range" | "multiple" | "single" |
positioning | PositioningOptions | { placement: "top" } |
lazyMount | boolean | - |
unmountOnExit | boolean | - |
DatePickerTrigger
Button that opens the date picker. button
| Prop | Type | Default |
|---|---|---|
asChild | boolean | - |
className | string | - |
DatePickerInput
Input with calendar icon that opens the date picker on click. input
| Prop | Type | Default |
|---|---|---|
placeholder | string | - |
index | number | - |
size | "sm" | "md" | "lg" | - |
className | string | - |
DatePickerContent
Wraps the calendar. Inherits --cell-size from calendar for date cell sizing.
| Prop | Type | Default |
|---|---|---|
className | string | - |
| Attribute | Default |
|---|---|
--cell-size | --spacing(8) |
DatePickerValue
Displays the selected date(s) as text. Use inside DatePickerTrigger. span
| Prop | Type | Default |
|---|---|---|
placeholder | string | - |
className | string | - |
asChild | boolean | - |
DatePickerTimer
Time input for selecting hour and minute. Use alongside the calendar for date-time selection. input
| Prop | Type | Default |
|---|---|---|
value | string | - |
defaultValue | string | - |
onValueChange | (details: ValueChangeDetails) => void | - |
className | string | - |
DatePickerPresetTrigger
Quick preset button for dates. button
| Prop | Type | Default |
|---|---|---|
className | string | - |
value | DateValue[] | - |
asChild | boolean | - |
For a complete list of props, see the Ark UI documentation.