Skip to main content

Upgrading to v9

DayPicker v9 includes significant updates to accessibility, styles, internationalization, and performance. If your app is on v8, you can usually upgrade in one pass by updating the package, CSS imports, controlled selection, custom styles, and any custom components.

See the changelog for the full list of changes.

Upgrading from v7

If you are upgrading from v7 or earlier, first follow the migration guide from v7 to v8, then follow this guide.

Upgrade checklist

Most apps can migrate in this order:

  1. Upgrade the package: install the latest react-day-picker package.
  2. Update CSS imports and styles: replace v8 CSS imports, CSS variables, class names, and styles keys.
  3. Add onSelect when using selected: make controlled selections explicit.
  4. Replace navigation boundary props: move from from* and to* props to startMonth, endMonth, and hidden.
  5. Update custom components: replace renamed component slots.
  6. Update formatters, labels, and tests: return strings from formatters and update ARIA label overrides or assertions.
  7. Replace deprecated aliases and types: remove names kept only for compatibility.

If your app wraps DayPicker in a shared Calendar or DatePicker component, update that wrapper first, then check the screens that use it.

1. Upgrade the package

Install the latest v9 release:

npm install react-day-picker@latest

If your app installed date-fns only because v8 required it as a peer dependency, you can remove it. Keep date-fns if you use it directly, for example for formatting dates or passing locales from date-fns/locale.

npm remove date-fns

2. Update CSS imports and styles

Replace the v8 bundled CSS import:

- import "react-day-picker/dist/style.css";
+ import "react-day-picker/style.css";

If you import the CSS module, update that import path as well:

- import classNames from "react-day-picker/dist/style.module.css";
+ import classNames from "react-day-picker/style.module.css";

The default CSS and the rendered markup changed in v9. If you only import DayPicker's default CSS and do not override it, updating the CSS import path may be enough. If your app has custom CSS selectors, CSS variables, classNames, or styles, review them carefully.

Common style updates:

  • CSS variables are now scoped under .rdp-root. Move overrides there if they were previously set on :root or .rdp.
  • The old day element is now day_button; the old cell element is now day.
  • Class names for several UI elements and day states changed.

Example class name prop update:

<DayPicker
classNames={{
- day_disabled: "day-disabled",
+ disabled: "day-disabled",
}}
/>

Example day cell and button update:

<DayPicker
classNames={{
- cell: "day-cell",
+ day: "day-cell",
- day: "day-button",
+ day_button: "day-button",
}}
/>

Example CSS selector update:

-.rdp-day_selected {
+.rdp-selected {
font-weight: bold;
}

-.rdp-cell {
+.rdp-day {
padding: 0;
}

-.rdp-day {
+.rdp-day_button {
border-radius: 999px;
}
Class name changes from v8

See the current class name keys in the UI enum.

3. Add onSelect when using selected

When you pass selected, DayPicker expects you to handle selection changes with onSelect. Add state in your component and pass the state setter or a custom handler.

+ const [selected, setSelected] = useState<Date | undefined>(undefined);

<DayPicker
mode="single"
selected={selected}
+ onSelect={setSelected}
/>

The same rule applies to mode="multiple" and mode="range":

const [range, setRange] = useState<DateRange | undefined>();

<DayPicker mode="range" selected={range} onSelect={setRange} />;

If you only need to mark days visually and do not want DayPicker to manage a selection mode, use custom modifiers instead of selected.

4. Replace navigation boundary props

The old navigation boundary props are deprecated in v9. Replace them with startMonth, endMonth, and hidden so your app is ready for v10.

Replace year boundaries:

<DayPicker
- fromYear={2010}
- toYear={2021}
+ startMonth={new Date(2010, 0)}
+ endMonth={new Date(2021, 11)}
/>

Replace month boundaries:

<DayPicker
- fromMonth={new Date(2010, 0)}
- toMonth={new Date(2021, 11)}
+ startMonth={new Date(2010, 0)}
+ endMonth={new Date(2021, 11)}
/>

Replace date boundaries:

<DayPicker
- fromDate={new Date(2010, 11, 3)}
+ startMonth={new Date(2010, 11)}
+ hidden={{ before: new Date(2010, 11, 3) }}
/>
<DayPicker
- toDate={new Date(2010, 11, 3)}
+ endMonth={new Date(2010, 11)}
+ hidden={{ after: new Date(2010, 11, 3) }}
/>

Use both startMonth or endMonth and hidden when you need to limit navigation and hide individual days outside the allowed range.

5. Update custom components

If you use the components prop, update custom component names and props.

Navigation icons now use one Chevron component:

<DayPicker
components={{
- IconRight: MyRightIcon,
- IconLeft: MyLeftIcon,
+ Chevron: (props) => {
+ if (props.orientation === "left") {
+ return <ChevronLeftIcon {...props} />;
+ }
+ return <ChevronRightIcon {...props} />;
+ },
}}
/>
Component changes from v8

See the custom components guide for details.

6. Update formatters, labels, and tests

Formatters now return strings. If a v8 formatter returned a React element, move the React rendering into a custom component.

<DayPicker
formatters={{
- formatCaption: (month) => <strong>{format(month, "LLLL y")}</strong>,
+ formatCaption: (month) => format(month, "LLLL y"),
}}
/>

Use a custom component when you need React markup:

<DayPicker
formatters={{
formatCaption: (month) => format(month, "LLLL y"),
}}
components={{
CaptionLabel: ({ children, ...props }) => (
<span {...props}>
<strong>{children}</strong>
</span>
),
}}
/>

Rename formatter and label aliases:

DayPicker's ARIA labels changed for accessibility. Update label overrides, translations, and tests that query buttons by accessible name.

Update test selectors as needed:

- screen.getByRole("button", { name: "Go to next month" });
+ screen.getByRole("button", { name: "Go to the Next Month" });

7. Replace deprecated aliases and types

Several v8 names still work in v9 for compatibility, but they are deprecated. Replace them with the current names before upgrading to v10.

Deprecated utility aliases

Deprecated TypeScript names

If you use TypeScript, replace deprecated v8 type names with the shorter current names.

- import type { DayPickerDefaultProps } from "react-day-picker";
+ import type { PropsBase } from "react-day-picker";
TypeScript type changes from v8

Useful search patterns

Use these patterns to find the most common v8 code that needs review:

rg "react-day-picker/dist/style|react-day-picker/dist/style.module|fromDate|toDate|fromMonth|toMonth|fromYear|toYear"
rg "selected=|initialFocus|onWeekNumberClick|onDayKeyUp|onDayKeyPress|onDayPointerEnter|onDayPointerLeave|onDayTouch"
rg "day_selected|day_disabled|day_today|day_range_|cell|table|tbody|head_cell|nav_button"
rg "IconLeft|IconRight|IconDropdown|DayContent|Caption|HeadRow|Row"
rg "formatMonthCaption|formatYearCaption|labelDay|labelCaption|isMatch|isDateInRange"
rg "DayPickerSingleProps|DayPickerMultipleProps|DayPickerRangeProps|DayPickerDefaultProps|useNavigation|useDayRender"