Skip to content
v2.5.0
English
Русский
Documentation

DayPicker

DayPicker is the lower-level day-grid subsystem used by the calendar stack.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
day-picker.tsx · 5 lines
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
Tested in
24 tests

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

  • DayPickerProps
  • DayPickerLayoutProps
  • DayPickerRowGroupProps
  • DayPickerRowProps
  • DayPickerItemProps
  • DayPickerCellProps
  • DayPickerCellContextData
  • DayPickerScrollerProps
  • DayPickerScrollerContentProps
  • DayPickerScrollerLayoutProps
  • DayPickerGridCell, DayPickerGridRow, DayPickerGridOptions

Minimal Composition

en-US
ru-RU
ar-EG
he-IL
minimal-composition.tsx · 13 lines
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

  • DayPickerLayout
  • DayPickerRowGroup
  • DayPickerRow
  • DayPickerItem
  • DayPickerCell: the cell, placed as the template of the layouts
  • DayPickerScroller
  • DayPickerScrollerContent
  • DayPickerScrollerLayout

Composition Order

  1. DayPicker
  2. either DayPickerLayout with a cell template as children (<DayPickerCell /> or a custom one) or custom row/item composition
  3. optional scroller wrapper for paged month navigation

Variants

  • Selection: single, multiple, range
  • Layout: static grid or scroller-based month paging
  • Period: explicit month (a YYYY-MM-DD string of any day in it) or the current month
  • Locale and calendar system: locale and calendar props, 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) are YYYY-MM-DD days in every calendar system; only the grid shape and labels follow calendar.

Common Mistakes

  • Reimplementing date-grid math instead of using createDayPickerGrid.
  • Passing numeric years, zero-based months, or timestamps instead of YYYY-MM-DD strings.
  • Losing selection visibility in custom DayPickerItem rendering.
  • 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.

one-month-of-days.tsx · 11 lines
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.

en-US
ru-RU
ar-EG
he-IL
selected-day.tsx · 28 lines
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.

visible-month-with-own-navigation.tsx · 67 lines
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.

several-days-in-a-form.tsx · 73 lines
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.

two-months-side-by-side.tsx · 42 lines
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.

range-too-long.tsx · 48 lines
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.

booking-window-and-closed-weekdays.tsx · 40 lines
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.

month-of-another-calendar-system.tsx · 29 lines
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.

overflow-days.tsx · 24 lines
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

  1. DayPicker
  2. optional scroller layer
  3. either DayPickerLayout or 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.

valid-composition-patterns.tsx · 24 lines
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.