- Accordion
- Advanced Toast
- AlertMessage
- ArrowButton
- Arrows
- Avatar
- Backdrop
- Basic Toast
- Breadcrumb
- Button
- CardBackground
- Card
- Carousel
- Chapters
- ChatBody
- ChatButton
- ChatHeader
- ChatInput
- ChatMessageActions
- ChatMessageEditor
- ChatMessage
- ChatTypingIndicator
- ChatWindowCsatPrompt
- ChatWindowQuote
- ChatWindow
- CheckboxSelect
- Checkbox
- Checkboxes
- Chip
- ChoiceExpander
- Clickable
- ContentReview
- Countdown
- CriteriaDates
- CriteriaParticipants
- CriteriaTransports
- Date Field
- Datepicker
- Dropdown
- ElasticHeight
- ExpandableCard
- ExpandableText
- Filter
- FloatingLabelTextField
- Form Control
- Form Label
- Frame
- HamburgerIcon
- Heading
- Image
- JourneyCard
- LayerHeader
- Layer
- Link
- Loader
- Number Field
- PageNavigation
- Pagination
- Paragraph
- Password
- Phone Field
- Popin
- Portal
- ProductThumbnail
- Promises
- Prose
- Radio Group
- RadioSelect
- Radio
- Range
- Scrollbar
- Select
- SidebarLayout
- SliderFrame
- SparkleWrapper
- Spinner
- Switch
- Table
- Tabs
- Tag
- TextField
- Toggle
- Tooltip
- ValidationMessage
Nov 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Dec 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Jan 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Feb 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Mar 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Apr 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
May 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Jun 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Jul 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Aug 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Sep 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Oct 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
'use client';
import { useState } from 'react';
import type { DateRange } from 'react-day-picker';
import { DeviceProvider } from '@/ui/contexts/devices/Device';
import { Datepicker } from '@/ui/Datepicker';
const today = new Date();
const makeDateId = (offset: number) => {
const d = new Date(today);
d.setDate(d.getDate() + offset);
return d.toISOString().slice(0, 10);
};
export function DatepickerDemo() {
const [selected, setSelected] = useState<DateRange | undefined>();
return (
<DeviceProvider device="all">
<Datepicker
fromMonth={today}
selected={selected}
onSelect={(range) => setSelected(range)}
labels={{
seeNextMonth: 'See {currentMonth} and {nextMonth}',
seePreviousMonth: 'See {previousMonth} and {currentMonth}',
}}
dates={[
{ id: makeDateId(3), price: '890 €', status: 'green' },
{ id: makeDateId(5), price: '1 200 €', status: 'orange' },
{ id: makeDateId(7), price: '760 €', status: 'outline' },
]}
statusLabels={{
green: 'Best price',
orange: 'Last availabilities',
outline: 'Available',
}}
/>
</DeviceProvider>
);
}
Installation
npx shadcn@latest add https://develop.trident-ui.pro.clubmed/r/datepicker.json
Dependencies
Install the peer dependencies:
npm install react-day-picker date-fns
The component reads the current breakpoint via useDevice, which requires DeviceProvider somewhere above it in the tree. In most apps this belongs at the root layout:
// app/layout.tsx (Next.js) or your root provider
import { DeviceProvider } from '@/ui/contexts/devices/Device';
export default function RootLayout({ children }) {
return (
<DeviceProvider device="all">
{children}
</DeviceProvider>
);
}You can also wrap just the Datepicker when DeviceProvider is not at the root:
import { DeviceProvider } from '@/ui/contexts/devices/Device';
import { Datepicker } from '@/ui/Datepicker';
<DeviceProvider device="all">
<Datepicker ... />
</DeviceProvider>Usage
Basic range selection
The minimum configuration: controlled state, a fromMonth to anchor the calendar, and the two navigation button labels.
'use client';
import { useState } from 'react';
import type { DateRange } from 'react-day-picker';
import { Datepicker } from '@/ui/Datepicker';
export function Component() {
const [selected, setSelected] = useState<DateRange | undefined>();
return (
<Datepicker
fromMonth={new Date()}
selected={selected}
onSelect={(range) => setSelected(range)}
labels={{
seeNextMonth: 'See {currentMonth} and {nextMonth}',
seePreviousMonth: 'See {previousMonth} and {currentMonth}',
}}
/>
);
}With a pre-selected range
Pass an initial selected value to open the calendar with a range already highlighted.
Nov 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Dec 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Jan 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Feb 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Mar 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Apr 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
May 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Jun 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Jul 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Aug 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Sep 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Oct 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
'use client';
import { useState } from 'react';
import type { DateRange } from 'react-day-picker';
import { DeviceProvider } from '@/ui/contexts/devices/Device';
import { Datepicker } from '@/ui/Datepicker';
// Build a date offset from today
const d = (offsetDays: number) => {
const date = new Date();
date.setDate(date.getDate() + offsetDays);
return date;
};
const id = (offsetDays: number) => d(offsetDays).toISOString().slice(0, 10);
export function DatepickerPreselectedDemo() {
const [selected, setSelected] = useState<DateRange>({
from: d(5),
to: d(12),
});
return (
<DeviceProvider device="all">
<Datepicker
fromMonth={new Date()}
selected={selected}
onSelect={(range) => setSelected(range ?? { from: undefined, to: undefined })}
labels={{
seeNextMonth: 'See {currentMonth} and {nextMonth}',
seePreviousMonth: 'See {previousMonth} and {currentMonth}',
}}
dates={[
{ id: id(3), price: '890 €', status: 'green' },
{ id: id(4), price: '910 €', status: 'green' },
{ id: id(8), price: '1 450 €', status: 'orange' },
{ id: id(9), price: '1 450 €', status: 'orange' },
{ id: id(14), price: '720 €', status: 'outline' },
{ id: id(15), price: '720 €', status: 'outline' },
{ id: id(20), price: '1 100 €', status: 'default' },
]}
statusLabels={{
green: 'Best price of the month',
orange: 'Last available dates',
outline: 'Available',
}}
/>
</DeviceProvider>
);
}
With price and status annotations
Each day can carry a price string and a status that controls its visual style. Pass the dates array and provide statusLabels for screen-reader announcements.
<Datepicker
fromMonth={new Date()}
selected={selected}
onSelect={setSelected}
labels={{ ... }}
dates={[
{ id: '2025-07-10', price: '890 €', status: 'green' },
{ id: '2025-07-11', price: '890 €', status: 'green' },
{ id: '2025-07-15', price: '1 450 €', status: 'orange' },
{ id: '2025-07-20', price: '720 €', status: 'outline' },
{ id: '2025-07-22', price: '1 100 €', status: 'default' },
]}
statusLabels={{
green: 'Best price of the month',
orange: 'Last available dates',
outline: 'Available',
}}
/>With disabled days
Pass any react-day-picker Matcher or array of Matcher values to disabledDays. Common patterns include blocking dates before today, specific dates, and date ranges.
Nov 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Dec 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Jan 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Feb 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Mar 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Apr 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
May 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Jun 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Jul 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Aug 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Sep 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Oct 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
'use client';
import { useState } from 'react';
import type { DateRange } from 'react-day-picker';
import { DeviceProvider } from '@/ui/contexts/devices/Device';
import { Datepicker } from '@/ui/Datepicker';
const d = (offsetDays: number) => {
const date = new Date();
date.setDate(date.getDate() + offsetDays);
return date;
};
export function DatepickerDisabledDaysDemo() {
const [selected, setSelected] = useState<DateRange | undefined>();
return (
<DeviceProvider device="all">
<Datepicker
fromMonth={new Date()}
selected={selected}
onSelect={(range) => setSelected(range)}
disabledDays={[
// Prevent selecting past dates
{ before: new Date() },
// Block specific unavailable dates
d(4),
d(5),
d(11),
d(12),
d(18),
d(19),
// Block out an entire period
{ from: d(25), to: d(30) },
]}
labels={{
seeNextMonth: 'See {currentMonth} and {nextMonth}',
seePreviousMonth: 'See {previousMonth} and {currentMonth}',
}}
/>
</DeviceProvider>
);
}
With duration tooltip
When the user has selected a start date and hovers over a potential end date, a tooltip shows how many nights that range would be. Provide a formatDurationsLabel function that receives the night count and returns the string to display.
Nov 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Dec 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Jan 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Feb 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Mar 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Apr 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
May 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Jun 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Jul 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Aug 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Sep 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Oct 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
'use client';
import { useState } from 'react';
import type { DateRange } from 'react-day-picker';
import { DeviceProvider } from '@/ui/contexts/devices/Device';
import { Datepicker } from '@/ui/Datepicker';
const d = (offsetDays: number) => {
const date = new Date();
date.setDate(date.getDate() + offsetDays);
return date;
};
const id = (offsetDays: number) => d(offsetDays).toISOString().slice(0, 10);
const pluralNights = (n: number) => (n === 1 ? '1 night' : `${n} nights`);
export function DatepickerDurationTooltipDemo() {
const [selected, setSelected] = useState<DateRange | undefined>();
return (
<DeviceProvider device="all">
<Datepicker
fromMonth={new Date()}
selected={selected}
onSelect={(range) => setSelected(range)}
formatDurationsLabel={pluralNights}
labels={{
seeNextMonth: 'See {currentMonth} and {nextMonth}',
seePreviousMonth: 'See {previousMonth} and {currentMonth}',
}}
dates={[
{ id: id(2), price: '1 090 €', status: 'green' },
{ id: id(3), price: '1 090 €', status: 'green' },
{ id: id(6), price: '1 480 €', status: 'orange' },
{ id: id(7), price: '1 480 €', status: 'orange' },
{ id: id(10), price: '850 €', status: 'outline' },
{ id: id(13), price: '920 €', status: 'default' },
]}
statusLabels={{
green: 'Best price',
orange: 'Last availabilities',
outline: 'Available',
}}
disabledDays={{ before: new Date() }}
/>
</DeviceProvider>
);
}
The tooltip appears above the hovered end date on desktop only (mouse events are not fired on touch).
Without prices (simple mode)
Omitting dates gives a clean calendar with no sub-labels. Useful when availability is expressed through disabledDays alone.
Nov 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Dec 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Jan 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Feb 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Mar 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Apr 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
May 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Jun 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Jul 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Aug 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Sep 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Oct 2027
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
'use client';
import { useState } from 'react';
import type { DateRange } from 'react-day-picker';
import { DeviceProvider } from '@/ui/contexts/devices/Device';
import { Datepicker } from '@/ui/Datepicker';
const d = (offsetDays: number) => {
const date = new Date();
date.setDate(date.getDate() + offsetDays);
return date;
};
export function DatepickerNoPricesDemo() {
const [selected, setSelected] = useState<DateRange | undefined>();
return (
<div className="space-y-16">
<DeviceProvider device="all">
<Datepicker
fromMonth={new Date()}
selected={selected}
onSelect={(range) => setSelected(range)}
disabledDays={[{ before: new Date() }, d(6), d(7), d(13), d(14)]}
labels={{
seeNextMonth: 'See {currentMonth} and {nextMonth}',
seePreviousMonth: 'See {previousMonth} and {currentMonth}',
}}
/>
</DeviceProvider>
{selected?.from && (
<p className="text-b3 text-center">
{selected.from.toLocaleDateString()}
{selected.to ? ` → ${selected.to.toLocaleDateString()}` : ' → pick an end date'}
</p>
)}
</div>
);
}
API Reference
Datepicker
| Prop | Type | Default | Description |
|---|---|---|---|
labels | Labels | — | Required. Accessible labels for the previous/next month navigation buttons. Supports token interpolation — see Labels. |
selected | DateRange | undefined | The current selection as { from?: Date; to?: Date }. The component is fully controlled: you own the state. |
onSelect | SelectRangeHandler | undefined | Fired every time the user clicks a date. Receives (range, triggerDate, modifiers, event). range may be undefined if the user deselects. |
dates | DayDataProps[] | [] | Per-day data objects that annotate individual days with a price and a status color. See DayDataProps. |
statusLabels | StatusLabelMap | undefined | Screen-reader text for each non-default status. If a day has status: 'green' and statusLabels.green is set, a visually-hidden <span> with that text is rendered inside the day button. |
disabledDays | Matcher | Matcher[] | undefined | Days that cannot be selected. Accepts any react-day-picker Matcher — a Date, a { before } / { after } / { from, to } object, or an array mixing any of the above. |
fromMonth | Date | new Date() | The earliest month the user can navigate to. Also the default starting month on desktop. The previous-month button is disabled when the displayed month equals this value. |
defaultMonth | Date | fromMonth | The month shown initially on desktop. Has no effect on mobile (the full scroll list always starts at fromMonth). |
maxNumberOfMonths | number | 12 | How many months to render on mobile. The scroll list always starts at fromMonth and ends maxNumberOfMonths months later. |
locale | Locale | undefined | A date-fns locale object. Affects weekday names, month names, and date formatting in navigation labels. |
formatDurationsLabel | (nights: number) => string | undefined | When provided, a tooltip appears above the hovered end date showing this label. Only fires on desktop (hover events). The function receives the integer night count. |
Labels
Navigation button labels support token interpolation. The tokens are replaced with locale-formatted month names at render time.
| Field | Tokens available | Example value |
|---|---|---|
seeNextMonth | {currentMonth}, {nextMonth} | "See {currentMonth} and {nextMonth}" |
seePreviousMonth | {previousMonth}, {currentMonth} | "See {previousMonth} and {currentMonth}" |
The resulting string becomes the aria-label on the navigation button, which is read aloud by screen readers. Choose a phrasing that is meaningful as a spoken sentence, e.g. "See January 2026 and February 2026".
On mobile the buttons are always hidden visually (hidden md:inline-block) but remain in the DOM with their labels so keyboard users can still navigate.
DayDataProps
Annotates a single calendar day. Pass an array of these objects to the dates prop.
| Field | Type | Description |
|---|---|---|
id | string | Required. The date to annotate, in ISO yyyy-MM-dd format (e.g. "2025-07-15"). Days not present in the array render with the default unstyled appearance. |
price | string | null | A pre-formatted price string displayed below the day number (e.g. "1 200 €"). Hidden when the day is part of a selected range. Pass null to explicitly show no price. |
status | "default" | "green" | "orange" | "outline" | Controls the background pill drawn behind the day number. See Day statuses for the visual mapping. |
StatusLabelMap
Maps each non-default status value to a screen-reader string. Only the statuses you include are announced.
interface StatusLabelMap {
green?: string; // e.g. "Best price of the month"
orange?: string; // e.g. "Last available dates"
outline?: string; // e.g. "Available"
}SelectRangeHandler
The full type of the onSelect callback:
type SelectRangeHandler = (
selected: DateRange | undefined,
triggerDate: Date,
modifiers: Modifiers,
e: React.MouseEvent | React.KeyboardEvent,
) => void;selected— the new range after the click.undefinedwhen the user deselects the last date. The component normalises the first click to{ from: date, to: undefined }to match v8 behaviour (react-day-picker v9 setsfrom === toon the first click internally).triggerDate— the exact date the user clicked.modifiers— the full react-day-picker modifier state fortriggerDate(e.g.selected,disabled,today).e— the original mouse or keyboard event.
Day statuses
Each status draws a different background on the day-number circle:
| Status | Visual | When to use |
|---|---|---|
default | No background — plain number | Date is available but has no special highlight |
green | Filled green pill (bg-green text-white) | Best-price day of the month |
orange | Filled orange rounded square (bg-orange text-white) | High-demand / last available dates |
outline | White rounded square with black border | Available but not the cheapest or most constrained |
When a day is disabled, all statuses are overridden with bg-sand text-middleGrey opacity-60.
When a day is selected (part of the highlighted range), the status background is suppressed — the selection highlight from the calendar CSS takes over.
Responsive behaviour
The component switches layout based on the medium breakpoint provided by DeviceProvider:
| Viewport | Layout | Navigation |
|---|---|---|
| Mobile | Vertical scroll list of maxNumberOfMonths months | Previous/next buttons hidden visually, but focusable |
| Desktop | Two months side by side | Previous/next buttons fully visible |
The medium breakpoint corresponds to min-width: 768px in the default Trident UI theme. DeviceProvider reads this via matchMedia, so the layout responds to window resize without a page reload.
Hover-range preview
On desktop, when the user has selected exactly one date (either from or to is set but not both), hovering over a candidate end date highlights the in-between days using the same rdp-range_middle class as the confirmed selection. This gives the user a visual preview of the range before committing.
The preview is implemented with custom day modifiers (hover_range_middle, hover_range_end) that are mapped to the CSS class names from the confirmed range. It is completely suppressed on mobile because mouseover events do not fire on touch screens.
CSS
The component ships its own stylesheet (Datepicker.css) imported at the top of Datepicker.tsx. It targets the rdp-* class names emitted by react-day-picker v9 and overrides them with Trident UI design tokens:
.rdp-root — container background (lightSand), border-radius, padding
.rdp-months — flex column on mobile, row on md+
.rdp-day_button — invisible border box that shows on focus / hover
.rdp-range_middle — range fill (white bg + black text)
.rdp-range_start — start-of-range rounded start corner
.rdp-range_end — end-of-range rounded end corner
.rdp-day[data-selected] — selected day (black bg, white text)
.rdp-day[data-disabled] — reduced opacity
The file uses @reference to pull in the global CSS variables, so Tailwind utility classes (@apply) resolve correctly. If you copy the component manually, make sure your build pipeline processes this file with the same Tailwind config.
Accessibility
- Month headings are rendered as
<p role="heading" aria-level="3" tabindex="-1">. After the user clicks a navigation button, focus moves programmatically to the heading of the newly visible first month so keyboard and screen-reader users receive immediate feedback. - Month grids (
<table>) are annotated witharia-labelledbypointing to their month heading'sid, so screen readers can announce which month's grid the user is navigating. - Navigation buttons carry full spoken labels produced by interpolating the
Labelstokens. On mobile the buttons arehidden md:inline-blockbut remain in the tab order. - Day status labels are injected as visually-hidden
<span class="sr-only">text inside each annotated day button. Screen readers will read e.g. "15, Best price of the month" rather than just "15". - Disabled days receive the standard
disabledHTML attribute on their button, which prevents focus and interaction without relying on visual styling alone.
Notes
Controlled-only
The Datepicker is fully controlled — it holds no internal selection state. You must always pass selected and onSelect together, otherwise clicking a date has no visible effect.
First-click normalisation
react-day-picker v9 sets from === to when the user clicks the very first date (where previously v8 only set from). The component normalises this by detecting the case and calling onSelect with { from: date, to: undefined } instead. This preserves the v8 interaction model without requiring consumer code changes.
fromMonth is the lower bound for navigation, not for selection
fromMonth prevents the user from navigating to an earlier month, but it does not disable the dates within that month. If you also want to disable dates before today, pass { before: new Date() } (or your custom lower bound) to disabledDays.
Price string formatting
The price field in DayDataProps is a pre-formatted display string — the component does not apply any currency logic. Format the string on the data side before passing it in (e.g. Intl.NumberFormat or a backend-provided string). Very long price strings are scaled down automatically using a CSS scale transform based on character count, so they do not overflow the day cell.
Client component
The Datepicker uses useState, useCallback, and useMemo. Add 'use client' at the top of any file that renders it in a Next.js App Router project.
On This Page
InstallationDependenciesUsageBasic range selectionWith a pre-selected rangeWith price and status annotationsWith disabled daysWith duration tooltipWithout prices (simple mode)API ReferenceDatepickerLabelsDayDataPropsStatusLabelMapSelectRangeHandlerDay statusesResponsive behaviourHover-range previewCSSAccessibilityNotesControlled-onlyFirst-click normalisationfromMonth is the lower bound for navigation, not for selectionPrice string formattingClient component