SuperAPI AI Directory

shadcn-errors-react-day-picker-v9

Frontend & websites

View repository

Updated 10-15 22:18

About this skill

react-day-picker v8 -> v9 Errors in the shadcn Calendar

A shadcn Calendar is a thin wrapper around react-day-picker. The react-day-picker 9.0.0 release (mid-2024) reshaped almost every public prop name, the classNames key system, the components-override surface, and the formatter contract. Every v8 prop name still parses as valid JSX in v9 (selectedDays, disabledDays, modifiers={[...]}, components={{ Caption }}), but the v9 DayPicker simply ignores them. The symptom is therefore SILENT : the page compiles, the calendar mounts, and nothing happens (no selection, no styles, no custom caption). TypeScript narrows the typed errors to a single rule on mode ; the rest manifests as visual breakage.

This skill maps each v8 syntax shape onto its v9 replacement, lists the TypeScript error messages that surface for the typed cases, and documents the canonical recovery flow when the shadcn-side components/ui/calendar.tsx itself pre-dates the v9 release.

Companion skills :

  • shadcn-syntax-calendar-datepicker (Batch B7) : the v9 API surface in full and the DatePicker composition recipe (Popover + Calendar + Input). Read this skill FIRST for the green-field v9 syntax ; this errors skill assumes you already know the v9 shape and need to migrate v8 code or diagnose a v8-shape regression.
  • shadcn-errors-cli-sync-mismatch : the canonical recovery flow uses npx shadcn@latest add calendar --overwrite when the shadcn-side calendar file is itself stale.
  • shadcn-errors-styling-conflicts : when CSS classes look right but the calendar is still unstyled, the react-day-picker/style.css import may have moved.

Quick Reference : v8 -> v9 Prop Rename Table

For every prop the calendar accepts, v9 either keeps the v8 name, renames it, or removes it entirely. This table is the single most important reference in this skill. ALWAYS consult it before changing any prop.

v8 prop / API v9 prop / API Type / Notes
selectedDays selected Date | Date[] | DateRange | undefined ; matches the mode
disabledDays disabled Matcher | Matcher[]
modifiers={[...]} modifiers={{ name: Matcher }} object of Matchers, NOT an array
modifiersStyles modifiersStyles unchanged, but keys map to v9 modifier names
modifiersClassNames modifiersClassNames unchanged, but keys map to v9 modifier names
components.Caption components.MonthCaption the caption above the month grid
components.IconLeft components.Chevron single component receives orientation: "left" | "right" | "up" | "down"
components.IconRight components.Chevron same as above
components.Row components.Week one row of days
components.HeadRow components.Weekdays the header row with weekday names
components.Day components.DayButton signature changed to ({ day, modifiers, ...props })
fromMonth startMonth Date
toMonth endMonth Date
fromDate startMonth + hidden matcher use hidden={{ before: Date }} to gray-out earlier days
toDate endMonth + hidden matcher use hidden={{ after: Date }}
month month unchanged (controlled current month)
defaultMonth defaultMonth unchanged (uncontrolled initial month)
numberOfMonths numberOfMonths unchanged
mode IMPLICIT (selectedDays: Date) mode="single" | "multiple" | "range" REQUIRED v9 demands explicit mode when selected is set
onSelect (per-mode) onSelect (per-mode) unchanged, but the value type is narrowed by mode
classNames keyed by free strings (day, cell, caption) classNames keyed by UI enum strings (day, day_button, cell REMOVED, month_caption, range_start etc.) see UI enum in references/methods.md
formatCaption: (m) => <strong>...</strong> formatCaption: (m) => "..." v9 formatters MUST return string, NOT JSX
direct date-fns import inside DayPicker dateLib adapter prop pass a DateLib adapter so locales / time-zones flow through the picker
react-day-picker/dist/style.css react-day-picker/style.css new import path, the old one 404s
DayPickerSingleProps PropsSingle renamed type
DayPickerMultipleProps PropsMulti renamed type
DayPickerRangeProps PropsRange renamed type
useNavigation useDayPicker renamed hook

Verified at https://daypicker.dev/v9/upgrading (2026-05-19).

Quick Reference : The mode Prop is REQUIRED in v9

In v8, selectedDays={date} IMPLIED mode="single". v9 removes the implicit form. The mode prop is now part of the union type that narrows selected and onSelect :

// CORRECT v9 : single
<Calendar mode="single" selected={date} onSelect={setDate} />

// CORRECT v9 : multiple
<Calendar mode="multiple" selected={dates} onSelect={setDates} />

// CORRECT v9 : range
<Calendar mode="range" selected={range} onSelect={setRange} />

// WRONG : mode missing, TypeScript error
<Calendar selected={date} onSelect={setDate} />
// Type '{ selected: Date; onSelect: ... }' is not assignable to type
// 'DayPickerProps'. Property 'mode' is missing.

Without mode, v9 falls back to no selection at all : the picker renders, dates are clickable, but the click never updates state. TypeScript catches this at compile time IF strict: true is on.

Quick Reference : classNames is Keyed by the UI Enum

v9 publishes a UI string enum that names every visual slot in the calendar (root, months, month, nav, button_previous, button_next, month_caption, dropdowns, caption_label, table, weekdays, weekday, week, day, day_button, range_start, range_middle, range_end, today, outside, disabled, hidden, week_number, week_number_header, etc.).

The v8 keys (cell, day, caption, head_row, nav_button) either moved (day is now the table cell, day_button is the button inside it) or vanished entirely. Use getDefaultClassNames() and merge with cn(...) so the shadcn defaults stay applied :

import { DayPicker, getDefaultClassNames } from "react-day-picker"
import { cn } from "@/lib/utils"

const defaultClassNames = getDefaultClassNames()

<DayPicker
  classNames={{
    root: cn("w-fit", defaultClassNames.root),
    month_caption: cn("...your tailwind...", defaultClassNames.month_caption),
    day: cn("...your tailwind...", defaultClassNames.day),
    day_button: cn("...your tailwind...", defaultClassNames.day_button),
    range_start: cn("rounded-l-md bg-accent", defaultClassNames.range_start),
    // any v8 key (`cell`, `head_row`) is ignored
  }}
/>

Verified at https://github.com/shadcn-ui/ui/blob/main/apps/v4/registry/new-york-v4/ui/calendar.tsx (2026-05-19) : the shadcn Calendar source itself uses getDefaultClassNames() and merges every v9 key.

Quick Reference : dateLib Adapter Replaces Direct date-fns

v8 imported date-fns directly inside DayPicker and accepted a locale prop. v9 introduces a dateLib adapter so the calendar can work with date-fns, dayjs, luxon, native Intl, or a custom adapter. The shadcn Calendar still accepts locale (forwarded to the default adapter), but for time-zones, custom week starts, or non-Gregorian calendars the dateLib prop is the v9 surface :

import { DayPicker } from "react-day-picker"
import { fr } from "date-fns/locale"

// CORRECT v9 : locale only
<DayPicker locale={fr} />

// CORRECT v9 : dateLib with time-zone
<DayPicker dateLib={{ ...defaultDateLib, timeZone: "Europe/Amsterdam" }} />

// WRONG : v8 used `useNavigation` hook to read the locale ; v9 renamed it
import { useDayPicker } from "react-day-picker"  // not useNavigation

See references/methods.md for the full DateLib signature.

Quick Reference : Symptoms-to-Fix Matrix

Match the symptom on the left to the exact v8 -> v9 cause on the right. ALWAYS run through this matrix BEFORE touching code.

Symptom Cause Fix
Calendar renders but no day is highlighted as selected selectedDays used instead of selected rename to selected, add mode="single"
Disabled days are still clickable disabledDays used instead of disabled rename to disabled
Custom month-header component never appears components.Caption used rename to components.MonthCaption
Left / right arrow icons are gone components.IconLeft / IconRight used merge into components.Chevron with orientation switch
TypeScript error : Property 'mode' is missing v9 demands mode add mode="single" | "multiple" | "range"
TypeScript error : selectedDays does not exist on type DayPickerProps v8 prop name rename to selected
Calendar is unstyled, looks like a raw HTML table react-day-picker/dist/style.css import 404s on v9, OR classNames uses v8 keys change import to react-day-picker/style.css AND rekey to v9 UI strings
modifiers.weekend not applied to any day modifiers={[{...}]} array form switch to object form modifiers={{ weekend: Matcher }}
Custom <Day> renderer errors with "Cannot read property of undefined" v8 Day signature switch to components.DayButton with ({ day, modifiers, ...props })
Range selection ignores fromMonth / toMonth boundaries v8 boundary props rename to startMonth / endMonth
formatCaption throws "Objects are not valid as a React child" v9 formatters must return string return a string ; for JSX use components.MonthCaption
Import error : Module not found: react-day-picker/dist/style.css v9 moved the CSS use react-day-picker/style.css
useNavigation is not a function v9 renamed the hook use useDayPicker

Quick Reference : Canonical Recovery Flow

If your shadcn-side components/ui/calendar.tsx PRE-dates the v9 release (check : does it import getDefaultClassNames ? does it set mode="single" defaults ? does it map Chevron ?), the file itself is v8-shape. Manual patching is fragile. The supported recovery is to re-run the shadcn CLI :

npx shadcn@latest diff calendar    # preview the v9 calendar source
npx shadcn@latest add calendar --overwrite   # rewrite to v9 shape

This regenerates components/ui/calendar.tsx with the v9 getDefaultClassNames() / components.Chevron / components.DayButton wiring. After overwrite, any consumer site that still passes v8 props through Calendar will surface TypeScript errors at the consumer site, which you fix using the rename table above.

Verified at https://github.com/shadcn-ui/ui/issues/4366 (2026-05-19, 97 reactions, open) : the canonical maintainer-recommended fix is to re-run shadcn add calendar --overwrite.

Decision Tree : Diagnosing a Broken Calendar After an Upgrade

START : Calendar broken after `react-day-picker@9` install
  |
  +-- Does TypeScript compile ?
  |    +-- NO : read the error
  |    |    +-- "Property 'mode' is missing" -> add `mode="single|multiple|range"`
  |    |    +-- "selectedDays does not exist" -> rename to `selected`
  |    |    +-- "disabledDays does not exist" -> rename to `disabled`
  |    |    +-- "DayPickerSingleProps does not exist" -> rename to `PropsSingle`
  |    +-- YES : continue
  |
  +-- Does Calendar render at all ?
  |    +-- NO : check the CSS import path (must be `react-day-picker/style.css`)
  |    +-- YES : continue
  |
  +-- Are styles applied ?
  |    +-- NO : `classNames` still keyed by v8 strings ; rekey using `UI` enum
  |    +-- YES : continue
  |
  +-- Does selection update on click ?
  |    +-- NO : `mode` missing ; selection is silently disabled
  |    +-- YES : continue
  |
  +-- Are custom captions / icons appearing ?
  |    +-- NO : `components.Caption` / `IconLeft` / `IconRight` ignored ; rename to MonthCaption / Chevron
  |    +-- YES : DONE

Cross-File Companion : When To Re-Add Calendar

If MORE THAN 3 v8 prop names appear in the project AND the components/ui/calendar.tsx file does NOT contain getDefaultClassNames, do NOT patch by hand. Run :

npx shadcn@latest add calendar --overwrite

Then fix the consumer call sites with the rename table. Patching a v8-shape calendar.tsx to v9 by hand creates a long tail of subtle bugs (classNames keys, components signatures, formatter contracts) that the CLI rewrite resolves in one shot.

Reference Files

  • references/methods.md : full v8 -> v9 API rename table, UI enum reference, DateLib adapter signature, Matcher type, components override map, formatter contract.
  • references/examples.md : v8 single-date code -> v9 equivalent ; v8 range -> v9 with DateRange ; v8 disabledDays -> v9 disabled ; v8 custom Caption -> v9 MonthCaption ; v8 modifiers array -> v9 modifiers object of Matchers ; dateLib with a custom locale.
  • references/anti-patterns.md : six recurring v8-shape-in-v9 failure modes with symptom, root cause, and verified fix.

Sources