Shadcn Datetime Picker
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
Quick Start
# 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.json
That’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.
→ See all install options
Why this picker?
Three things this repo does that other shadcn datetime pickers don’t:
- Real timezone support, not just UTC offset. Powered by
react-day-picker v9’s TZDate, 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.
DateTimeInput handles Android Gboard, iOS keyboards, and IME composition correctly via beforeinput — 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. |
Highlights
- Timezone-aware — render and edit any IANA timezone, powered by
react-day-picker’s TZDate.
- Keyboard-first — arrow keys step segments, digit keys auto-advance, type
a/p to toggle period.
- Soft-keyboard ready — handles Android Gboard, IME composition, and
beforeinput quirks.
- Form-friendly — works out-of-the-box with
react-hook-form, exposes forwardRef and inline error.
- Min/Max constraints —
DateTimePicker and SimpleTimePicker disable 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.json aliases and CSS variables.
- Copy-paste ownership — three single-file components, no runtime dependency on this package.
Install via shadcn registry
Option 1 — shadcn CLI (recommended)
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.json
The 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 your components.json aliases.
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 an overrides entry 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.
Option 2 — copy & paste
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/:
Usage
DateTimePicker
import { DateTimePicker } from '@/components/datetime-picker';
export default function Example() {
const [date, setDate] = useState<Date | undefined>();
return <DateTimePicker value={date} />;
}
With a timezone
<DateTimePicker value={date} timezone="Asia/Ho_Chi_Minh" />
With min / max bounds
const min = useMemo(() => subHours(new Date(), 2), []);
const max = useMemo(() => addMonths(new Date(), 2), []);
<DateTimePicker value={date} min={min} max={max} />
Custom trigger
<DateTimePicker
value={date}
renderTrigger={({ value, setOpen }) => (
<Button variant="outline" => 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}
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} hideError />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
SimpleTimePicker
import { SimpleTimePicker } from '@/components/simple-time-picker';
const [time, setTime] = useState<Date>(new Date());
<SimpleTimePicker value={time} use12HourFormat />
Showcase
Live Storybook stories — each link is a deep-link to the story:
⚠️ 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.
FAQ
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.
Tech stack
Built on top of shadcn/ui, react-day-picker v9, date-fns, and Tailwind CSS.
Local development
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)
Contributing
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.
License
MIT — do whatever you want, attribution appreciated.
评论