How to Add a Calendar to a React App
Step-by-step guide to adding a copy-and-own Calendar to any React project with the Drivn CLI — single date, range, dropdown, and locale support.
A calendar is deceptively involved to build well. The grid has to lay out weeks correctly across month boundaries, handle keyboard navigation, support single-date and range selection, and stay localizable — and most teams reach for a library the moment they need more than a styled <input type="date">. The question is which library, and how much of its weight and API you inherit.
Drivn answers it by writing the component into your own repository instead of installing a runtime package. The CLI copies a calendar.tsx file that wraps react-day-picker — the proven grid-logic library — in a thin, fully typed surface with no other UI dependencies. After install the file is yours: you can read it end to end, restyle the styles object, and ship it today. Because it relies on react-day-picker hooks it is a client component, marked 'use client', but the page around it can still render on the server.
This guide adds the Calendar to an existing React app in about ten minutes — install the CLI, copy the component, render it with controlled state, switch to range mode and the dropdown and week-number variants, then customize the styles. It works the same in Vite + React or a Next.js App Router app. For the Next.js-specific walkthrough see the Next.js Calendar guide; for live patterns see the Calendar examples.
Prerequisites
Before installing the Calendar, confirm your React project has the three things Drivn assumes: Tailwind CSS v4 installed and processing your CSS, TypeScript configured (the component ships as a .tsx file), and a @/ path alias pointing at your source directory. If you scaffolded with create-next-app, npm create vite, or npx drivn@latest create, all three are already wired. For a custom setup, check the compilerOptions.paths entry in tsconfig.json; the installation page lists the minimal config. The Calendar pulls in react-day-picker for grid logic and lucide-react for the navigation chevrons — the CLI adds both to your package.json during install if they are missing. No PostCSS plugins or Tailwind config changes are required.
Step 1 — Install Drivn via the CLI
Run the CLI from your project root to add the Calendar source file. The command prompts once for your install directory (defaulting to src/components/ui/), writes calendar.tsx, and adds react-day-picker and lucide-react to your package.json if they are not already present. No global config file is created — the Calendar is a TypeScript file in your repo that you edit like any other component. Confirm the file landed in your editor, then commit it. If your project uses a monorepo layout or a non-standard path, the CLI docs cover the flags for targeting a custom location during install.
1 # add the Calendar to your existing React project 2 npx drivn add calendar 3 4 # verify the file was written 5 ls src/components/ui/calendar.tsx
Step 2 — Import and render with controlled state
Open the page where the Calendar should live and import it from your UI directory. The default render is an inline month grid with arrow navigation. Because the Calendar wraps react-day-picker, which tracks selection with hooks, the source begins with 'use client' — so hold the selected date in useState and bind selected and onSelect. In a Next.js App Router project, keep this in a small client component and render it as an island; in a Vite app it works anywhere. See the Calendar docs for the full prop table and the examples page for complete patterns.
1 'use client' 2 import { useState } from 'react' 3 import { Calendar } from '@/components/ui/calendar' 4 5 export function BookingDate() { 6 const [date, setDate] = useState<Date | undefined>() 7 return ( 8 <Calendar 9 selected={date} 10 onSelect={setDate} 11 /> 12 ) 13 }
Step 3 — Range, dropdown, and week-number variants
Single-date selection is the default; three more modes are one prop away. For from–to inputs use the Calendar.Range sub-component, exposed through the same import via Object.assign in the source — it holds a { from, to } value, and you import the DateRange type to type your state. The variant prop switches layout: variant="dropdown" swaps the caption for month and year <select> menus (bounded by fromYear and toYear), and variant="weekNumbers" adds an ISO week-number column for scheduling views. Each is a plain prop, so you change layouts without touching the source. The date picker wraps this same Calendar in a popover trigger if you need a compact field.
1 import { Calendar, type DateRange } from '@/components/ui/calendar' 2 3 // range selection 4 <Calendar.Range selected={range} onSelect={setRange} /> 5 6 // dropdown month/year navigation 7 <Calendar 8 variant="dropdown" 9 fromYear={1950} 10 toYear={2035} 11 selected={date} 12 onSelect={setDate} 13 /> 14 15 // ISO week numbers 16 <Calendar variant="weekNumbers" selected={date} onSelect={setDate} />
Step 4 — Customize the styles object
Because the Calendar lives in your codebase, customization is a source edit rather than a prop API. Open src/components/ui/calendar.tsx and find the const styles object near the top — its root key holds the panel's padding, background, border, and radius, and the class-name keys feed react-day-picker's classNames slots. Change root to restyle the surface, or adjust the day and range keys to change the selected-day treatment. Every class reads from your Tailwind tokens, so editing the color tokens in globals re-themes every Calendar instance at once instead of per-call. To localize the grid, pass a locale from react-day-picker/locale.
1 // styles.root from src/components/ui/calendar.tsx 2 const styles = { 3 root: 'p-3 bg-card border border-border rounded-[10px] text-sm', 4 // ...classNames, dropdownOverrides, rangeActive 5 }
Install Drivn in one command
Copy the source into your project and own every line. Zero runtime dependencies, pure React + Tailwind.
Frequently asked questions
Yes. Set the project up with TypeScript and Tailwind v4 first, then run npx drivn add calendar. The Calendar has no dependency on Next.js or any router — it renders anywhere React and Tailwind reach the DOM. Vite + React is the most common non-Next setup and works without extra configuration; for CRA, add Tailwind v4 and a tsconfig.json and the CLI drops the .tsx file straight in.
The Calendar is a client component — it is marked 'use client' because it wraps react-day-picker, which manages selection state with hooks. Your surrounding page and layout still render on the server. The usual pattern is a small client component holding the Calendar and its useState, rendered as an island inside an otherwise server-rendered route. Next.js inserts the client boundary at the import automatically.
Use the Calendar.Range sub-component exposed through the same import. It renders react-day-picker in range mode and expects a selected value shaped as { from, to }. Import the DateRange type alongside the component to type your state. Drivn rounds the start and end caps and fills the middle days with the accent token once both endpoints are chosen, so the selection reads clearly.
Pass a locale prop to the Calendar. Import a locale object from react-day-picker/locale — cs for Czech, de for German, and so on — and the month names, weekday headers, and week-start day all update to match. No Drivn-specific localization table is involved; any locale the underlying react-day-picker library ships works without extra wiring.

