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

DayPicker

DayPicker is the lower-level day-grid subsystem used by the calendar stack.

Patterns

Static Month Grid

When to use:

  • one known month is enough, for example picking a day inside a billing period

Composition notes:

  • use DayPickerLayout and pass month explicitly, so the grid does not depend on today
  • constrain the period with from and to instead of hiding days

Trade-offs:

  • simple and stable
  • no navigation; use a scroller or Calendar when users move between months
static-month-grid.tsx · 29 lines
import { DayPicker, DayPickerCell, DayPickerLayout, Text } from '@vega-ui/react';
import { useState } from 'react';

const dateFormat = new Intl.DateTimeFormat('en-US', { month: 'long', day: 'numeric', timeZone: 'UTC' });

export default function StaticMonthGrid() {
  const [day, setDay] = useState<string | null>(null);

  return (
    <div style={{ display: 'grid', gap: 'var(--spacing-6)', justifyItems: 'start', width: '100%' }}>
      <DayPicker
        aria-label='Payment day, November 2026'

Swipable Month Paging

When to use:

  • users move between months with swipe or snap paging and the app draws its own header

Composition notes:

  • use DayPickerScroller with DayPickerScrollerContent and DayPickerScrollerLayout
  • onChangePeriod reports the first day of the month scrolled into view; store it and pass it back as month, so the header and the grid agree
  • keep active in state as well and move it into the new month, otherwise the grid can lose its tab stop
  • reach for Calendar when the standard header, month view, and year view are enough

Trade-offs:

  • tactile, and any header layout is possible
  • the app owns navigation state that Calendar would handle
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';

Custom Grid Rendering

When to use:

  • the row and cell structure itself must change, which a cell template cannot do

Composition notes:

  • build rows with createDayPickerGrid instead of reimplementing date math; pass the same calendar as the picker
  • date is undefined for an empty overflow cell; day is the day number in the calendar system
  • set disabled and excluded on cells outside the month, as the built-in cell does
  • here a Monday-to-Friday grid: weekend cells are not rendered, and weeks without a workday of the month are dropped

Trade-offs:

  • full control over markup
  • labels, week start, and overflow handling are the app's job; prefer a cell template when only the content changes
custom-grid-rendering.tsx · 38 lines
import {
  CalendarBaseWeekLabel,
  CalendarBaseWeekLabels,
  DayPicker,
  DayPickerItem,
  DayPickerRow,
  DayPickerRowGroup,
  createDayPickerGrid,
} from '@vega-ui/react';

const MONTH = '2026-11-01';
const WORK_DAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'];

Cell Template

When to use:

  • day cells need extra content such as a price, a status, or a count

Composition notes:

  • children of DayPickerLayout and DayPickerScrollerLayout are rendered once per day inside a cell context
  • read the day with useDayPickerCellContext(): { col, date, fields, label, inMonth }; date is undefined for an empty overflow cell
  • render the content inside DayPickerCell so the cell keeps selection, focus, and disabled states
  • take day numbers from label or fields, never from the ISO date string; they differ in non-Gregorian calendars
  • look data up by date, which is the same in every calendar system
  • extra content that is only visual (dots, colors) needs words in the cell's aria-label

Trade-offs:

  • keeps the grid, keys, and keyboard behavior of the layout
  • richer cells can crowd small sizes
cell-template.tsx · 38 lines
import {
  DayPicker,
  DayPickerCell,
  DayPickerLayout,
  useDayPickerCellContext,
} from '@vega-ui/react';

const events: Record<string, string[]> = {
  '2026-11-04': ['Design review', 'Sprint planning'],
  '2026-11-12': ['Release 3.2'],
  '2026-11-18': ['Team offsite', 'Retro', 'Demo day'],
};

Comparison

Quick Decision Rule

Use DayPicker when you need low-level day-grid selection behavior. Use Calendar when you need a higher-level calendar experience.

DayPicker vs Calendar

  • Use DayPicker for low-level day-grid composition.
  • Use Calendar for the higher-level calendar subsystem.

DayPicker vs MonthPicker / YearPicker

  • Use DayPicker for actual dates.
  • Use MonthPicker or YearPicker for month/year selection surfaces.

Choose This Component When

  • you are extending calendar-family internals
  • you need day-grid selection with custom composition

Do Not Choose This Component When

  • you want a higher-level calendar abstraction with less manual composition

Troubleshooting

The Rendered Month Does Not Match The Expected Period

Symptom

The UI shows a month grid, but it is not the period users expected.

Likely Cause

The visible month, the calendar system, or the scroller's defaultMonth are not aligned.

How To Verify

  • inspect the effective month, calendar, and the value passed to onChangePeriod

Fix

Use one YYYY-MM-DD month as the source of truth and pass the same calendar to every part that renders it.

Custom Day Rendering Loses Selection Or Focus Clarity

Symptom

Dates still work logically, but users cannot tell which day is active or selected.

Likely Cause

Custom rendering discarded important visual state.

How To Verify

  • test focus, selected, disabled, and excluded states in the custom layout

Fix

Preserve the item state affordances from the default day item pattern.

Scrollable Month Paging Feels Fragile

Symptom

Swiping between months works sometimes, but period labels or focus feel unstable.

Likely Cause

Scroller period mapping and rendered month content are not synchronized tightly enough.

How To Verify

  • log the month passed to onChangePeriod while paging

Fix

Derive labels from the month reported by onChangePeriod instead of computing it from the scroller index.