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

YearPicker

YearPicker is the year-selection subsystem of the calendar family.
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
year-picker.tsx · 5 lines
import { YearPicker, YearPickerCell, YearPickerLayout } from '@vega-ui/react';

<YearPicker aria-label='Year' year='2026-01-01' defaultSelected='2026-01-01' style={{ width: '100%' }}>
  <YearPickerLayout><YearPickerCell /></YearPickerLayout>
</YearPicker>
Browser support
Widely available
  • Chrome 111
  • Edge 111
  • Firefox 113
  • Safari 15.4
  • Safari on iOS 15.4
Bundle size
Tested in
17 tests

Summary

YearPicker is the year-selection subsystem of the calendar family. It builds on DataGridPicker, supports static or scroller-based year grids, and pages years in fixed-size groups (period, 12 by default) for decade-like navigation.

Imports

import {
  YearPicker,
  YearPickerCell,
  YearPickerItem,
  YearPickerLayout,
  YearPickerRow,
  YearPickerRowGroup,
  YearPickerScroller,
  YearPickerScrollerContent,
  YearPickerScrollerLayout,
  createYearPickerGrid,
  useYearPickerCellContext,
  useYearPickerContext,
  useYearPickerScrollerContext,
  type YearPickerCellContextData,
  type YearPickerCellProps,
  type YearPickerItemProps,
  type YearPickerLayoutProps,
  type YearPickerProps,
  type YearPickerRowGroupProps,
  type YearPickerRowProps,
  type YearPickerScrollerContentProps,
  type YearPickerScrollerLayoutProps,
  type YearPickerScrollerProps,
} from '@vega-ui/react';

Exported Types

  • YearPickerProps
  • YearPickerLayoutProps
  • YearPickerRowGroupProps
  • YearPickerRowProps
  • YearPickerItemProps
  • YearPickerCellProps
  • YearPickerCellContextData
  • YearPickerScrollerProps
  • YearPickerScrollerContentProps
  • YearPickerScrollerLayoutProps

Minimal Composition

en-US
ru-RU
ar-EG
he-IL
minimal-composition.tsx · 13 lines
import { YearPicker, YearPickerCell, YearPickerLayout } 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 (
    <YearPicker aria-label='Year' locale={locale} year='2026-01-01' style={{ width: '100%' }}>
      <YearPickerLayout>
        <YearPickerCell />
      </YearPickerLayout>
    </YearPicker>
  );

Required Parts

  • YearPicker: root year-selection subsystem

Optional Parts

  • YearPickerLayout
  • YearPickerRowGroup
  • YearPickerRow
  • YearPickerItem
  • YearPickerCell: the cell, placed as the template of the layouts
  • YearPickerScroller
  • YearPickerScrollerContent
  • YearPickerScrollerLayout

Composition Order

  1. YearPicker
  2. either YearPickerLayout with a cell template as children (<YearPickerCell /> or a custom one) or custom row/item composition
  3. optional scroller layer for paged year ranges

Variants

  • Selection: single, multiple, range
  • Layout: static grid or scroller-based year paging
  • Period: explicit visible year (a YYYY-MM-DD string of any day in it) or the current year
  • Locale and calendar system: locale and calendar props, defaulting to the runtime locale and the Gregorian calendar; eras are handled by the adapter

Edge Cases

  • Large year ranges require extra testing for focus, paging, and visible context.
  • year controls the visible period, not just selection.
  • Scroller mode pages by period years; a period that differs from the layout's rows × cols shows the wrong range.

Common Mistakes

  • Treating year ranges as unbounded without checking usability.
  • Letting parent calendar rules and year-picker bounds drift apart.
  • Using the low-level year picker where a higher-level calendar mode switch would be clearer.

Examples

Basic

Basic: a page of years

YearPickerLayout renders a page of rows × cols years (12 by default) that starts with the year given by year. Cells are keyed by the first day of each year, so defaultSelected is '2026-01-01'. The grid has no visible title, so it gets an aria-label.

a-page-of-years.tsx · 11 lines
import { YearPicker, YearPickerCell, YearPickerLayout } from '@vega-ui/react';

export default function APageOfYears() {
  return (
    <YearPicker aria-label='Model year' locale='en-US' year='2026-01-01' defaultSelected='2026-01-01' style={{ width: '100%' }}>
      <YearPickerLayout>
        <YearPickerCell />
      </YearPickerLayout>
    </YearPicker>
  );
}

Controlled/Stateful

Controlled/Stateful: selected year

selected and onSelectCell make the selection controlled. The scroller pages through years, and year keeps the page of the selected year in view; null means nothing is selected.

selected-year.tsx · 28 lines
import {
  Text,
  YearPicker,
  YearPickerCell,
  YearPickerScroller,
  YearPickerScrollerContent,
  YearPickerScrollerLayout,
} from '@vega-ui/react';
import { useState } from 'react';

export default function SelectedYear() {
  const [year, setYear] = useState<string | null>('2024-01-01');

Form/Integration

Form/Integration: year range filter

selection='range' picks a span of years. The form submits the first year as is and the end as the last day of its year, reads both back with FormData, reuses the linked hint for the empty check, and clears the state on reset.

year-range-filter.tsx · 79 lines
import {
  Button,
  Fieldset,
  FieldsetLegend,
  HelperText,
  Text,
  YearPicker,
  YearPickerCell,
  YearPickerLayout,
} from '@vega-ui/react';
import { FormEvent, useId, useState } from 'react';

Layout/Overlay

Layout/Overlay: paged years with own buttons

The scroller exposes prev() and next() through apiRef, so the page can be changed from buttons outside the grid, and reports the page in view with onChangePeriod for the range label. Keep period on the scroller equal to the layout's rows × cols, and move active into the new page when the old active year scrolls away.

paged-years-with-own-buttons.tsx · 70 lines
import {
  Icon,
  IconButton,
  type IndexedSnapScrollerApiRef,
  Text,
  YearPicker,
  YearPickerCell,
  YearPickerScroller,
  YearPickerScrollerContent,
  YearPickerScrollerLayout,
} from '@vega-ui/react';
import { ChevronLeft, ChevronRight } from '@vega-ui/icons';

Error

Error: under-age birth year

Here the birth year is not bounded with from / to; the app checks the age after the pick and turns the hint linked to the grid into an error. Prefer bounds when a year can never be valid (see Paged Year Range); validate when the user needs to see why.

under-age-birth-year.tsx · 46 lines
import {
  HelperText,
  YearPicker,
  YearPickerCell,
  YearPickerScroller,
  YearPickerScrollerContent,
  YearPickerScrollerLayout,
} from '@vega-ui/react';
import { useId, useState } from 'react';

const MIN_AGE = 18;
const CURRENT_YEAR = 2026;

Disabled

Disabled: specific years

An array of year starts disables exactly those years, here fiscal years that are already closed; to disables the future ones. The reason is written next to the grid and linked with aria-describedby.

specific-years.tsx · 32 lines
import {
  HelperText,
  YearPicker,
  YearPickerCell,
  YearPickerLayout,
} from '@vega-ui/react';
import { useId } from 'react';

const closedYears = ['2022-01-01', '2023-01-01'];

export default function SpecificYears() {
  const hintId = useId();

Edge

Edge: era change in the Japanese calendar

With a calendar system from createCalendarSystem (see Calendar System Adapter), years follow that calendar. In the Japanese calendar 2019 is split: Heisei 31 runs from January 1 to April 30 and Reiwa 1 starts on May 1, so the page shows two cells for it, keyed '2019-01-01' and '2019-05-01' and labeled 平成31年 and 令和元年. Year arithmetic, paging, and from / to treat them as two years.

era-change-in-the-japanese-calendar.tsx · 14 lines
import { createCalendarSystem } from '@vega-ui/i18n';
import { YearPicker, YearPickerCell, YearPickerLayout } from '@vega-ui/react';

const japaneseCalendar = createCalendarSystem('japanese');

export default function EraChangeInTheJapaneseCalendar() {
  return (
    <YearPicker aria-label='Year' calendar={japaneseCalendar} locale='ja-JP' year='2018-01-01' style={{ width: '100%' }}>
      <YearPickerLayout>
        <YearPickerCell />
      </YearPickerLayout>
    </YearPicker>

Edge: years counted from another epoch

A calendar that only renumbers Gregorian years can extend GregorianCalendar (see Extending GregorianCalendar). With the Thai Buddhist calendar the cell keyed '2026-01-01' is labeled พ.ศ. 2569 in th-TH; keys, from, and to stay Gregorian ISO days.

years-counted-from-another-epoch.tsx · 14 lines
import { createCalendarSystem } from '@vega-ui/i18n';
import { YearPicker, YearPickerCell, YearPickerLayout } from '@vega-ui/react';

const buddhistCalendar = createCalendarSystem('buddhist');

export default function YearsCountedFromAnotherEpoch() {
  return (
    <YearPicker aria-label='Year' calendar={buddhistCalendar} locale='th-TH' year='2026-01-01' to='2026-01-01' style={{ width: '100%' }}>
      <YearPickerLayout>
        <YearPickerCell />
      </YearPickerLayout>
    </YearPicker>

Anatomy

Overview

YearPicker wraps DataGridPicker for year values and can render either static year grids or scroller-driven paged year ranges.

Required Parts

YearPicker

Required. Owns year selection behavior and visible year context.

Optional Parts

YearPickerLayout

Optional default year-grid renderer.

YearPickerCell

Cell template. Children of YearPickerLayout and YearPickerScrollerLayout are required and rendered once per year inside a cell context read by useYearPickerCellContext(); usually <YearPickerCell />. Put custom content inside YearPickerCell to keep selection, focus, and disabled states.

YearPickerRowGroup, YearPickerRow, YearPickerItem

Optional low-level grid composition parts.

YearPickerScroller, YearPickerScrollerContent, YearPickerScrollerLayout

Optional paged year-range scroller infrastructure.

Composition Order

  1. YearPicker
  2. optional scroller layer
  3. either YearPickerLayout or custom row/item composition

Valid Composition Patterns

The scroller composition: YearPickerScroller › YearPickerScrollerContent › YearPickerScrollerLayout › cell template, inside YearPicker. Keep year in state and update it from onChangePeriod, so the root always knows the page in view.

valid-composition-patterns.tsx · 24 lines
import {
  YearPicker,
  YearPickerCell,
  YearPickerScroller,
  YearPickerScrollerContent,
  YearPickerScrollerLayout,
} from '@vega-ui/react';
import { useState } from 'react';

export default function ValidCompositionPatterns() {
  const [year, setYear] = useState('2026-01-01');

Invalid Composition Patterns

Scroller period that differs from the layout page size

The user sees one period while the paging logic thinks it is on another.

Custom layouts that ignore the provided year helpers

Year ordering and paging semantics become harder to keep correct.