import { DayPicker, DayPickerCell, DayPickerLayout } from '@vega-ui/react';
<DayPicker aria-label='November 2026' locale='en-GB' month='2026-11-01' defaultSelected='2026-11-03'>
<DayPickerLayout><DayPickerCell /></DayPickerLayout>
</DayPicker>- Browser support
- Widely available
- Chrome 111
- Edge 111
- Firefox 113
- Safari 15.4
- Safari on iOS 15.4
- Bundle size
- Oklab and OkLCh — comes from the design tokens
- Scroll snap
- scrollend — has a fallback in the code, not counted in the minimum
- scrollbar-width — covered by a -webkit- fallback, not counted in the minimum
- Intl.Locale
- Intl.Locale info — has a fallback in the code, not counted in the minimum
- Resize observer
- Logical properties
- :focus-visible
- :where()
- contain
- scroll-behavior
Summary
DayPicker is the lower-level day-grid subsystem used by the calendar stack. It builds on DataGridPicker and supports single, multiple, and range date selection while exposing layout, row, item, and scroller parts.
Imports
import {
DayPicker,
DayPickerCell,
DayPickerItem,
DayPickerLayout,
DayPickerRow,
DayPickerRowGroup,
DayPickerScroller,
DayPickerScrollerContent,
DayPickerScrollerLayout,
createDayPickerGrid,
useDayPickerCellContext,
useDayPickerContext,
useDayPickerScrollerContext,
type DayPickerGridCell,
type DayPickerGridOptions,
type DayPickerGridRow,
type DayPickerCellContextData,
type DayPickerCellProps,
type DayPickerItemProps,
type DayPickerLayoutProps,
type DayPickerProps,
type DayPickerRowGroupProps,
type DayPickerRowProps,
type DayPickerScrollerContentProps,
type DayPickerScrollerLayoutProps,
type DayPickerScrollerProps,
} from '@vega-ui/react';Exported Types
DayPickerPropsDayPickerLayoutPropsDayPickerRowGroupPropsDayPickerRowPropsDayPickerItemPropsDayPickerCellPropsDayPickerCellContextDataDayPickerScrollerPropsDayPickerScrollerContentPropsDayPickerScrollerLayoutPropsDayPickerGridCell,DayPickerGridRow,DayPickerGridOptions
Minimal Composition
import { DayPicker, DayPickerCell, DayPickerLayout } from '@vega-ui/react';
export const locales = ['en-US', 'ru-RU', 'ar-EG', 'he-IL'];
export default function MinimalComposition({ locale = 'en-US' }: { locale?: string }) {
return (
<DayPicker aria-label='November 2026' locale={locale} month='2026-11-01'>
<DayPickerLayout>
<DayPickerCell />
</DayPickerLayout>
</DayPicker>
);Required Parts
DayPicker: root day-selection subsystem
Optional Parts
DayPickerLayoutDayPickerRowGroupDayPickerRowDayPickerItemDayPickerCell: the cell, placed as the template of the layoutsDayPickerScrollerDayPickerScrollerContentDayPickerScrollerLayout
Composition Order
DayPicker- either
DayPickerLayoutwith a cell template as children (<DayPickerCell />or a custom one) or custom row/item composition - optional scroller wrapper for paged month navigation
Variants
- Selection:
single,multiple,range - Layout: static grid or scroller-based month paging
- Period: explicit
month(aYYYY-MM-DDstring of any day in it) or the current month - Locale and calendar system:
localeandcalendarprops, defaulting to the runtime locale and the Gregorian calendar
Edge Cases
- Overflow-day inclusion changes both visual density and selection behavior.
- Disabled, selected, excluded, and focused dates must remain distinguishable.
- Scrollable month paging adds another synchronization layer for visible period state.
- Cell keys (
selected,from,to,disabled,active) areYYYY-MM-DDdays in every calendar system; only the grid shape and labels followcalendar.
Common Mistakes
- Reimplementing date-grid math instead of using
createDayPickerGrid. - Passing numeric years, zero-based months, or timestamps instead of
YYYY-MM-DDstrings. - Losing selection visibility in custom
DayPickerItemrendering. - Treating it like a consumer-ready calendar instead of a low-level picker subsystem.
Examples
Basic
Basic: one month of days
DayPicker is the low-level day grid without a header; DayPickerLayout renders the month given by month. Cells are keyed by ISO days, so defaultSelected is an ISO day too. locale sets the first day of the week, and the grid needs an aria-label because it has no visible title.
import { DayPicker, DayPickerCell, DayPickerLayout } from '@vega-ui/react';
export default function OneMonthOfDays() {
return (
<DayPicker aria-label='March 2026' locale='en-GB' month='2026-03-01' defaultSelected='2026-03-15'>
<DayPickerLayout>
<DayPickerCell />
</DayPickerLayout>
</DayPicker>
);
}Controlled/Stateful
Controlled/Stateful: selected day
selected and onSelectCell make the selection controlled; in single mode null means "controlled, nothing selected", which the clear button relies on.
import {
Button,
DayPicker,
DayPickerCell,
DayPickerLayout,
Text,
} from '@vega-ui/react';
import { useState } from 'react';
export const locales = ['en-US', 'ru-RU', 'ar-EG', 'he-IL'];
export default function SelectedDay({ locale = 'en-US' }: { locale?: string }) {Controlled/Stateful: visible month with own navigation
With a scroller the month is swipeable. Keep month in state and update it from onChangePeriod, so a custom header stays in sync with swipes and its buttons can move the grid. DayPicker does not move its active cell when month changes, so the example keeps active in state and moves it to the new month when the old active day is out of view.
import {
DayPicker,
DayPickerCell,
DayPickerScroller,
DayPickerScrollerContent,
DayPickerScrollerLayout,
Icon,
IconButton,
Text,
} from '@vega-ui/react';
import { ChevronLeft, ChevronRight } from '@vega-ui/icons';
import { useState } from 'react';Form/Integration
Form/Integration: several days in a form
selection='multiple' picks independent days. The picker has no form value, so the days go out as hidden inputs with one name and come back with FormData.getAll. The "at least one day" check runs on submit and reuses the hint linked with aria-describedby; reset clears the state through the form's reset event.
import {
Button,
DayPicker,
DayPickerCell,
DayPickerLayout,
Fieldset,
FieldsetLegend,
HelperText,
Text,
} from '@vega-ui/react';
import { FormEvent, useId, useState } from 'react';
Layout/Overlay
Layout/Overlay: two months side by side
One DayPicker can hold several layouts, so a range can be picked across two months. Give each layout its month and a distinct offset, which keeps row indexes unique across the grid; an aria-label on each layout names its month for screen readers.
import {
DayPicker,
DayPickerCell,
DayPickerLayout,
Text,
} from '@vega-ui/react';
import { useState } from 'react';
const months = ['2026-11-01', '2026-12-01'];
const monthFormat = new Intl.DateTimeFormat('en-US', { month: 'long', year: 'numeric', timeZone: 'UTC' });
const rangeFormat = new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', timeZone: 'UTC' });
Error
Error: range too long
A range picker cannot limit the length of a range by itself. Count the nights in the app and turn the hint linked to the grid into an error while the range is too long.
import {
DayPicker,
DayPickerCell,
DayPickerLayout,
HelperText,
} from '@vega-ui/react';
import { useId, useState } from 'react';
const DAY = 86_400_000;
const MAX_NIGHTS = 14;
function countNights(start?: string, end?: string) {Disabled
Disabled: booking window and closed days
from and to disable every day outside the window, and the disabled predicate closes Sundays and a holiday; an array of ISO days works too when only specific days are closed. Disabled days cannot be selected or focused, so the rule is written next to the grid and linked with aria-describedby.
import {
DayPicker,
DayPickerCell,
DayPickerLayout,
HelperText,
} from '@vega-ui/react';
import { useId } from 'react';
const holidays = ['2026-11-26'];
function isClosed(day: string) {
if (new Date(day).getUTCDay() === 0) return true;Edge
Edge: month of another calendar system
With a calendar system from createCalendarSystem (see Calendar System Adapter), the grid shows a month of that calendar: Farvardin 1405 has 31 days, starts on the ISO day 2026-03-21, and the fa-IR week starts on Saturday. month is any ISO day inside the month; dir='rtl' on an ancestor mirrors the grid and arrow keys.
import { createCalendarSystem } from '@vega-ui/i18n';
import { DayPicker, DayPickerCell, DayPickerLayout, Text } from '@vega-ui/react';
import { useState } from 'react';
const persianCalendar = createCalendarSystem('persian');
const persianMonth = new Intl.DateTimeFormat('fa-IR-u-ca-persian', { month: 'long', year: 'numeric', timeZone: 'UTC' });
export default function MonthOfAnotherCalendarSystem() {
const [day, setDay] = useState<string | null>('2026-03-21');
return (
<div dir='rtl' lang='fa-IR' style={{ display: 'grid', gap: 'var(--spacing-6)', justifyItems: 'start', width: '100%' }}>Edge: overflow days
includeOverflowDays fills empty cells with days of the neighbouring months. DayPickerCell renders them disabled and excluded from selection; the template dims them through inMonth.
import {
DayPicker,
DayPickerCell,
DayPickerLayout,
useDayPickerCellContext,
} from '@vega-ui/react';
function OverflowDay() {
const { inMonth } = useDayPickerCellContext();
if (inMonth) return <DayPickerCell />;
Anatomy
Overview
DayPicker wraps DataGridPicker and adds day-grid-specific context: the visible month (first day as a YYYY-MM-DD string) and the calendar system. Layout can be static or scroller-driven.
Required Parts
DayPicker
Required. Owns day-grid selection mode and visible period context.
Optional Parts
DayPickerLayout
Optional default day-grid renderer.
DayPickerCell
Cell template. Children of DayPickerLayout and DayPickerScrollerLayout are required and rendered once per day inside a cell context read by useDayPickerCellContext(); usually <DayPickerCell />. Put custom content inside DayPickerCell to keep selection, focus, and disabled states.
DayPickerRowGroup, DayPickerRow, DayPickerItem
Optional low-level grid composition parts.
DayPickerScroller, DayPickerScrollerContent, DayPickerScrollerLayout
Optional scroller-based paging infrastructure for month navigation.
Composition Order
DayPicker- optional scroller layer
- either
DayPickerLayoutor custom row/item composition
Valid Composition Patterns
The scroller composition: DayPickerScroller › DayPickerScrollerContent › DayPickerScrollerLayout › cell template, inside DayPicker. The scroller starts at month of the root and scrolls to it when it changes, so keep month in state and update it from onChangePeriod, and the root always knows the month in view.
import {
DayPicker,
DayPickerCell,
DayPickerScroller,
DayPickerScrollerContent,
DayPickerScrollerLayout,
} from '@vega-ui/react';
import { useState } from 'react';
export default function ValidCompositionPatterns() {
const [month, setMonth] = useState('2026-11-01');
Invalid Composition Patterns
Custom grid math replacing helper functions casually
Selection and excluded-day logic can drift from the intended calendar behavior.
Scroller composition without a consistent period-mapping contract
Visible month and rendered month become hard to keep aligned.