build69
build69  /  Components  /  Date & Time  /  YearPicker

YearPicker

v1.0.3Updated 19 Oct 2026

Single-YEAR picker: a Popover-anchored grid of years behind an outline button trigger, four to a row, twelve to a page, with prev/next paging and fromYear/toYear bounds. Shows พ.ศ. or ค.ศ. with the same era toggle chip as the calendar pickers, and the value it reports is ALWAYS the Common Era number (2026, never 2569) whichever era is on screen. Full keyboard grid (arrows, Home/End, PageUp/PageDown, roving tabindex). Works controlled (year/onYearChange) or uncontrolled (defaultYear).

Preview

Live · 14 stories
Single YearOpen ↗
▸Why this is recommendedyearpickeryearpickerfiscal+8

YearPicker — single year.

Recommended for+14
  • ✓Picking a fiscal year
  • ✓Choosing a budget year
  • ✓Selecting a tax year
  • ✓เลือกปีงบประมาณ (Thai fiscal year in พ.ศ.)
  • ✓Filtering a report by year
  • ✓Year of manufacture or model year
  • ✓Graduation or birth year in a form
  • ✓Any field where the day and month are meaningless
Tags+6
yearpickeryearpickerfiscalbudgettaxbuddhistthaierapopovergridsingle
Features+4
  • Picks ONE year — no day, no month, no calendar grid to pull in
  • The value is always the Common Era year, whichever era the grid shows (the ERP rule: database in ค.ศ., screen in พ.ศ.)
  • Thai Buddhist Era display with the BE/CE era toggle chip; the choice sticks across reopens and is readable/settable via calendarMode
  • showEraChip offers the switch on a gregorian field too
  • fromYear / toYear bounds: years outside stay visible and disabled, and the pager stops at the bounds
  • yearsPerPage (default 12) and blocks aligned to a multiple of it, so a year always sits in the same block
  • Keyboard grid: arrows move by one and by a row, Home/End reach the ends of a block, PageUp/PageDown move a block, and an arrow off the edge pages the grid
  • Opens focused on the selected year rather than on the pager chevron
  • clearable={false} for a required field — a second click re-picks instead of emptying
  • Controlled (year) or uncontrolled (defaultYear), with value/onValueChange aliases
  • Composable footer slot via children
  • i18n: labels prop plus whole Thai built-ins behind locale="th"; year digits follow the app LocaleProvider for non-Thai locales
  • Labelled by FormField: the trigger takes the id, aria-describedby and aria-invalid, and fills the field column (standalone w-40)
Not recommended for−2
  • ✕Picking a full date (use DatePicker)
  • ✕Picking a month within a year (use DatePicker with a month caption, or Calendar)
  • ✕A span of years (use two YearPickers, or DateRangePicker for real dates)
  • ✕A long list of non-contiguous years (use Select or Combobox)

Install

Ask your agent, or call the MCP tool directly. The resolved bundle is written into your project.

get_registry_item({ name: "YearPicker" })

Usage

import { YearPicker } from '@/components/inputs/date-picker';

// Thai ERP: the grid shows พ.ศ. 2569, setYear receives 2026.
<YearPicker year={year} onYearChange={setYear} calendar="buddhist" locale="th" />

Tick variants in the left menu — each adds a tab with that variant’s real story source, the same code the preview above renders.

Props

PropTypeRequired
year

Selected year as a COMMON ERA number (controlled) — 2026, never 2569, whichever era the grid shows

number—
onYearChange

Fires with the Common Era year, or undefined when the field is cleared

(year: number | undefined) => void—
value

Alias of `year`, matching the value/onValueChange convention of the other inputs; `year` wins when both are set

number—
onValueChange

Alias of `onYearChange`; fired alongside it

(year: number | undefined) => void—
defaultYear

Initial value for uncontrolled usage (ignored when `year`/`value` is provided)

number—
placeholder

Trigger text when no year is selected (default "Pick a year", or "เลือกปี" under locale="th")

string—
fromYear

Earliest selectable year, in Common Era

number—
toYear

Latest selectable year, in Common Era

number—
yearsPerPage

Years on one page of the grid (default 12, four to a row). Blocks align to a multiple of it

number—
clearable

Whether clicking the selected year clears the field (default true, matching the day pickers). Set false on a required field, where an accidental second click emptying it is worse than having no way to clear from the grid

boolean—
align

Popover alignment

'start' | 'center' | 'end'—
children

Footer content rendered under the grid, beside the era chip

React.ReactNode—
calendar

The era the field shows by DEFAULT; "buddhist" renders พ.ศ. years and puts the era chip in the footer

'gregory' | 'buddhist'—
calendarMode

Which era the grid is SHOWING, as a controlled value. Pair with onCalendarModeChange to remember the user's choice across opens or sessions. Presentation only — the reported year is Common Era either way. The deprecated "gregorian" spelling is accepted here too and normalised on arrival, so an era a host stored under the old word can be fed straight back in without a type error (fb_e8de80fb); `onCalendarModeChange` always reports the house spelling

'gregory' | 'buddhist'—
onCalendarModeChange

Fires with the era the user switched the grid to

(mode: 'gregory' | 'buddhist') => void—
showEraChip

Show the era chip even when `calendar` is gregorian, so a ค.ศ. field can be switched to พ.ศ. (default false)

boolean—
locale

Locale for the built-in strings. locale="th" supplies the whole Thai set (placeholder, era chip พ.ศ./ค.ศ., pager labels); an explicit `labels` or `placeholder` still wins

'en' | 'th'—
labels

Override the built-in UI strings (i18n): placeholder, be, ce, toggleCalendarEra, previousYears, nextYears, yearGrid

Partial<YearPickerLabels>—
disabled

Disables the trigger

boolean—
invalid

Mark the field invalid (destructive ring); aria-invalid wins when both are set

boolean—
id

DOM id set on the trigger button; FormField clones its field id in, and its label then names the control

string—
aria-label

Accessible name of the trigger when there is no visible label

string—
aria-labelledby

Id(s) of the element(s) that label the trigger; wins over a FormField label

string—
aria-describedby

Id(s) of hint or error text describing the trigger (FormField clones this in)

string—
aria-invalid

Mark the trigger invalid; FormField clones this in from its error prop

boolean—
aria-required

Accepted (FormField clones it in) but not rendered: ARIA does not allow it on a button. The label text "required" is part of the name instead

boolean—

Related