Skip to content
Drivn logoDrivn
5 min read

React Calendar Component Examples

Drop-in React Calendar examples: single date, range, week numbers, year dropdown, and locale. Zero runtime UI deps — plain react-day-picker under Tailwind.

A date picker lives inside virtually every application that touches scheduling — booking flows, invoice editors, analytics filters, deadline inputs. The Calendar component in Drivn is the primitive those experiences build on. It is a thin styling layer over react-day-picker, with variants for the three shapes most apps need: a clean month grid, a week-numbered grid for payroll and planning UIs, and a year/month dropdown for picking birthdays or far-future dates.

This page collects the usage patterns that come up in shipped products. Each snippet is copy-paste ready and stays consistent with the Calendar source that the Drivn CLI writes into your repo. The imports use the same @/components/ui/calendar path the CLI installs under, so the code compiles the moment you add the component.

If you need a popover-wrapped calendar instead of the bare grid, use the DatePicker component, which composes Calendar inside a trigger with open/close behavior. The examples below stay at the Calendar level on purpose — many apps render the grid inline, inside a card, a sidebar, or a booking widget, rather than behind a popover. Pick the example closest to your case and change one line to wire in your state.

Single-date selection

The default Calendar variant renders a month grid with the current month's days, navigated via left and right chevrons in the caption. Pass selected as a Date | undefined and onSelect as the setter — react-day-picker handles the day clicks and calls back with the clicked date or undefined when the user clicks the same day twice (the deselect gesture).

Because the Calendar source lives in your repo after install, adding custom styling or an extra disabled day is a local edit rather than a theme override. The snippet below is the minimum viable integration: a controlled useState<Date | undefined> bound to the component, with no wrapper div or extra state.

1import { useState } from "react"
2import { Calendar } from "@/components/ui/calendar"
3
4export default function Page() {
5 const [date, setDate] = useState<Date | undefined>()
6
7 return (
8 <Calendar
9 selected={date}
10 onSelect={setDate}
11 />
12 )
13}

Week numbers and dropdown variants

The variant prop switches between three visual shapes without changing any other behavior. variant="weekNumbers" adds an ISO week number column on the left, which payroll, invoicing, and scheduling tools often require. variant="dropdown" replaces the month/year caption with native select elements bounded by fromYear and toYear, which is the right default for birthday pickers, deadline inputs far from today, or any case where the user must jump across years without clicking through twelve months.

Both variants share the same selected, onSelect, and disabled props — you swap variant and nothing else changes. Under the hood, Drivn forwards showWeekNumber: true or captionLayout: 'dropdown' to react-day-picker, plus a few extra classNames that restyle the caption.

1<Calendar
2 variant="weekNumbers"
3 selected={date}
4 onSelect={setDate}
5/>
6
7<Calendar
8 variant="dropdown"
9 fromYear={1950}
10 toYear={2100}
11 selected={date}
12 onSelect={setDate}
13/>

Range selection

Calendar.Range is a compound child on the same root component — same import, same selected / onSelect contract, different type. selected is DateRange | undefined where DateRange = { from: Date; to?: Date } (Drivn re-exports the type from react-day-picker). The first click sets from; the second click sets to. Clicking a day outside the current range resets it and starts a new one.

The styling for mid-range days uses the bg-accent token so the highlight tracks your theme. When the range is a single day (from === to), Drivn skips the range styling so the calendar does not render a half-pill. For a range picker that opens from a trigger button instead of rendering inline, see DatePicker.Range.

1import { useState } from "react"
2import { Calendar, type DateRange } from "@/components/ui/calendar"
3
4export default function Page() {
5 const [range, setRange] = useState<DateRange | undefined>()
6
7 return (
8 <Calendar.Range
9 selected={range}
10 onSelect={setRange}
11 />
12 )
13}

Localization

Drivn re-exports the Locale type from react-day-picker, and you can pass any locale object to the locale prop. Weekday headers, month names, and the first day of the week all update — a Czech locale shifts the grid to start on Monday and renders "Led / Úno / Bře" at the top. No additional configuration is needed; Drivn forwards the locale object directly into DayPicker.

This means your i18n setup is the same one you already use for dates elsewhere in the app. Import a locale from react-day-picker/locale — dozens are pre-built — and pass it in. If your translation layer already knows what locale is active, wire it through a React context and the Calendar picks it up automatically. For tokens that affect the grid appearance, see theming.

1import { useState } from "react"
2import { Calendar } from "@/components/ui/calendar"
3import { cs } from "react-day-picker/locale"
4
5export default function Page() {
6 const [date, setDate] = useState<Date | undefined>()
7
8 return (
9 <Calendar
10 locale={cs}
11 selected={date}
12 onSelect={setDate}
13 />
14 )
15}

Disabling dates

Pass a disabled prop with a matcher — a single Date, an array of dates, a { from, to } range, or a predicate (date: Date) => boolean — and those days become unclickable and render in a dimmer muted color. This is the same matcher API as react-day-picker, so anything that works in its docs works here without translation.

A common pattern: disable past dates in a booking picker, or disable weekends in a workday scheduler. The predicate form is the most flexible — pass (day) => day < today to block past dates, or (day) => [0, 6].includes(day.getDay()) to block weekends. Because the matcher is evaluated per day, there is no performance cost to checking against a dynamic cutoff.

1import { useState } from "react"
2import { Calendar } from "@/components/ui/calendar"
3
4export default function Page() {
5 const [date, setDate] = useState<Date | undefined>()
6 const today = new Date()
7 today.setHours(0, 0, 0, 0)
8
9 return (
10 <Calendar
11 selected={date}
12 onSelect={setDate}
13 disabled={(day) => day < today}
14 />
15 )
16}
Get started

Install Drivn in one command

Copy the source into your project and own every line. Zero runtime dependencies, pure React + Tailwind.

npx drivn@latest create

Requires Node 18+. Works with npm, pnpm, and yarn.

Enjoying Drivn?
Star the repo on GitHub to follow new component releases.
Star →

Frequently asked questions

No. The Calendar source imports DayPicker from react-day-picker and three chevron icons from lucide-react. That is the full runtime. There is no Radix, no cva, no clsx wrapper, no floating-ui — just the grid rendered and styled with Tailwind. If you already have lucide-react installed, the per-page cost after tree-shaking is react-day-picker plus a thin wrapper.

Yes — the default use case is inline. Render <Calendar> directly inside a card, a sidebar, or a booking panel and it shows as a regular block element. If you want it behind a trigger button with open/close logic, use the DatePicker component instead, which composes Calendar with the popover behavior and keyboard dismissal already wired.

Pass defaultMonth as a Date. The calendar opens to that month but leaves selected undefined so no day is highlighted. This is useful when you want to nudge the user toward a particular period without committing to a date — for example, opening a vacation booking picker on next month instead of the current one, or a quarterly report picker at the start of Q1.

Calendar renders the month grid inline and is a primitive you compose. DatePicker wraps Calendar in a trigger button plus a controlled popover, handling outside-click and Escape for you. If your UI shows the grid always (sidebar, card), use Calendar. If the grid should open from a button, use DatePicker — or build your own trigger and reuse Calendar inside it.

The Calendar file starts with "use client" because react-day-picker uses useState and event handlers. You can import it from a Server Component, but it renders on the client. If your page is a Server Component, leave the import where it is — Next.js draws the client boundary correctly based on the use client directive at the top of the file.