The most flexible date and time picker for shadcn/ui — installed in one command, fully owned by your project.
Drop-in date, time, and date-time pickers built the shadcn way — copy the source, own the styles, ship faster. No black-box dependency, just three self-contained files you can read, fork, and bend to your design system.
→ Open the live demo · → View Storybook stories
# Add the full datetime picker (calendar + time + timezone) — one command, owns the source.
npx shadcn@latest add https://shadcn-datetime-picker-pro.vercel.app/r/datetime-picker.jsonThat's it. The shadcn CLI installs button, popover, scroll-area, plus date-fns, react-day-picker@^9, lucide-react, and drops the picker into components/datetime-picker.tsx per your components.json aliases.
Three things this repo does that other shadcn datetime pickers don't:
- Real timezone support, not just UTC offset. Powered by
react-day-pickerv9'sTZDate, you can render and edit dates in any IANA timezone (Asia/Ho_Chi_Minh,America/New_York, …) without manually converting Date objects. - Soft-keyboard / IME-aware segmented input.
DateTimeInputhandles Android Gboard, iOS keyboards, and IME composition correctly viabeforeinput— most pickers break on these. - Install with one shadcn CLI command. No npm dependency to lock yourself into. The CLI fetches the registry JSON, resolves shadcn primitives + npm peer deps, and drops the source into your project.
| Component | What it does |
|---|---|
DateTimePicker |
Calendar + time popover. Min/max bounds, timezone, custom trigger. |
DateTimeInput |
Segmented keyboard-first input (think iOS-style). Arrow stepping, IME, RHF integration. |
SimpleTimePicker |
Compact dropdown time picker. 12h / 24h, hour-minute-second columns. |
- Timezone-aware — render and edit any IANA timezone, powered by
react-day-picker'sTZDate. - Keyboard-first — arrow keys step segments, digit keys auto-advance, type
a/pto toggle period. - Soft-keyboard ready — handles Android Gboard, IME composition, and
beforeinputquirks. - Form-friendly — works out-of-the-box with
react-hook-form, exposesforwardRefand inline error. - Min/Max constraints —
DateTimePickerandSimpleTimePickerdisable out-of-range dates and times in the calendar grid. - Custom trigger — render the picker behind any button, badge, or your own component.
- Fully styled with Tailwind — uses your project's
components.jsonaliases and CSS variables. - Copy-paste ownership — three single-file components, no runtime dependency on this package.
Install any component (and its shadcn + npm dependencies) in one command:
# DateTimePicker (the full picker)
npx shadcn@latest add https://shadcn-datetime-picker-pro.vercel.app/r/datetime-picker.json
# DateTimeInput (segmented keyboard input)
npx shadcn@latest add https://shadcn-datetime-picker-pro.vercel.app/r/datetime-input.json
# SimpleTimePicker (time-only dropdown)
npx shadcn@latest add https://shadcn-datetime-picker-pro.vercel.app/r/simple-time-picker.jsonThe CLI will:
- Add the required shadcn components (
button,popover,scroll-area…). - Install the npm dependencies (
date-fns,react-day-picker@^9,lucide-react, …). - Drop the source file into your
components/folder per yourcomponents.jsonaliases.
Heads-up on lint warnings. The shadcn CLI strips file-top directives during install (tracked upstream as shadcn-ui/ui#9206, fix in PR #8823). If your project uses strict ESLint/Biome rules, you may see warnings on
any, unused vars, or React-hooks rules in the installed file. Workaround: add anoverridesentry to your config, e.g.// .eslintrc.json "overrides": [ { "files": ["**/components/datetime-picker.tsx", "**/components/datetime-input.tsx", "**/components/simple-time-picker.tsx"], "rules": { "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-unused-vars": "off", "react-hooks/exhaustive-deps": "off" } } ]The credit JSDoc and
'use client'directive are preserved.
If you prefer the manual route:
- Install the shadcn primitives:
npx shadcn@latest add button popover scroll-area- Install the npm peer dependencies:
yarn add date-fns react-day-picker@^9 lucide-react
# or, if you use the segmented input
yarn add react-hook-form- Copy the file(s) you need into
components/:
import { DateTimePicker } from '@/components/datetime-picker';
export default function Example() {
const [date, setDate] = useState<Date | undefined>();
return <DateTimePicker value={date} onChange={setDate} />;
}<DateTimePicker value={date} onChange={setDate} timezone="Asia/Ho_Chi_Minh" />const min = useMemo(() => subHours(new Date(), 2), []);
const max = useMemo(() => addMonths(new Date(), 2), []);
<DateTimePicker value={date} onChange={setDate} min={min} max={max} /><DateTimePicker
value={date}
onChange={setDate}
renderTrigger={({ value, setOpen }) => (
<Button variant="outline" onClick={() => setOpen(true)}>
{value?.toLocaleString() ?? 'Pick a date'}
</Button>
)}
/>A keyboard-first segmented input — type digits, use arrow keys to step, a/p to toggle AM/PM.
import { DateTimeInput } from '@/components/datetime-input';
<DateTimeInput
value={date}
onChange={setDate}
format="dd/MM/yyyy hh:mm aa"
timezone="UTC"
/>With react-hook-form — bind value and onChange explicitly so RHF tracks the controlled value (the component does not currently surface onBlur/name, so don't spread {...field}):
const form = useForm({ defaultValues: { startsAt: new Date() } });
<FormField
control={form.control}
name="startsAt"
render={({ field }) => (
<FormItem>
<FormLabel>Starts at</FormLabel>
<FormControl>
<DateTimeInput value={field.value} onChange={field.onChange} hideError />
</FormControl>
<FormMessage />
</FormItem>
)}
/>import { SimpleTimePicker } from '@/components/simple-time-picker';
const [time, setTime] = useState<Date>(new Date());
<SimpleTimePicker value={time} onChange={setTime} use12HourFormat />Live Storybook stories — each link is a deep-link to the story:
- Date + Time picker in form —
react-hook-formintegration - Timezone story — IANA timezone switching
- Min/Max bounds — disabled out-of-range dates
- Segmented input — keyboard-first iOS-style input
- Custom trigger — render behind any UI element
⚠️ TODO trước khi merge: verify chính xác slug của các story trên Storybook live (đoạn--<slug>). Tao đoán slug dựa trên story name từ README hiện tại — có thể có slug không khớp.
Does this work with Tailwind v4? Yes. The components use shadcn-style CSS variables, which are Tailwind v3 and v4 compatible.
Can I use this without react-hook-form?
Yes. RHF is only needed for DateTimeInput's form integration example. The component itself is fully controlled via value / onChange.
How does this differ from the official shadcn date-picker?
shadcn/ui's date-picker is a basic Calendar + Popover composition without time selection or timezone support. This repo adds time, timezone, segmented input, and IME handling.
Why react-day-picker v9 specifically?
v9 ships TZDate and a major API rewrite that simplifies controlled timezone-aware behavior. We don't support v8.
Built on top of shadcn/ui, react-day-picker v9, date-fns, and Tailwind CSS.
yarn install
yarn dev # Next.js sandbox at http://localhost:3000
yarn storybook # Storybook at http://localhost:6006
yarn test # Vitest watch mode
yarn registry:build # Regenerate public/r/*.json (chained into Vercel build)Issues and PRs are very welcome. If you find a bug or have a feature request, please open an issue. For larger changes, drop a quick proposal first so we can align on the approach.
Looking to contribute? Check out the good first issue label.
MIT — do whatever you want, attribution appreciated.
