YearPicker
v1.0.3Updated 19 Oct 2026Single-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▸Why this is recommendedyearpickeryearpickerfiscal+8
YearPicker — single year.
- ✓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
- 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)
- ✕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.
Usage
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
yearSelected year as a COMMON ERA number (controlled) — 2026, never 2569, whichever era the grid shows
—onYearChangeFires with the Common Era year, or undefined when the field is cleared
—valueAlias of `year`, matching the value/onValueChange convention of the other inputs; `year` wins when both are set
—onValueChangeAlias of `onYearChange`; fired alongside it
—defaultYearInitial value for uncontrolled usage (ignored when `year`/`value` is provided)
—placeholderTrigger text when no year is selected (default "Pick a year", or "เลือกปี" under locale="th")
—fromYearEarliest selectable year, in Common Era
—toYearLatest selectable year, in Common Era
—yearsPerPageYears on one page of the grid (default 12, four to a row). Blocks align to a multiple of it
—clearableWhether 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
—alignPopover alignment
—childrenFooter content rendered under the grid, beside the era chip
—calendarThe era the field shows by DEFAULT; "buddhist" renders พ.ศ. years and puts the era chip in the footer
—calendarModeWhich 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
—onCalendarModeChangeFires with the era the user switched the grid to
—showEraChipShow the era chip even when `calendar` is gregorian, so a ค.ศ. field can be switched to พ.ศ. (default false)
—localeLocale for the built-in strings. locale="th" supplies the whole Thai set (placeholder, era chip พ.ศ./ค.ศ., pager labels); an explicit `labels` or `placeholder` still wins
—labelsOverride the built-in UI strings (i18n): placeholder, be, ce, toggleCalendarEra, previousYears, nextYears, yearGrid
—disabledDisables the trigger
—invalidMark the field invalid (destructive ring); aria-invalid wins when both are set
—idDOM id set on the trigger button; FormField clones its field id in, and its label then names the control
—aria-labelAccessible name of the trigger when there is no visible label
—aria-labelledbyId(s) of the element(s) that label the trigger; wins over a FormField label
—aria-describedbyId(s) of hint or error text describing the trigger (FormField clones this in)
—aria-invalidMark the trigger invalid; FormField clones this in from its error prop
—aria-requiredAccepted (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
—