Skip to main content

YearPicker

Year selector. The value is January 1 of the selected year in UTC-ISO form — for example, picking 2026 yields "2026-01-01T00:00:00.000Z".

import { YearPicker } from '@kalyx/react';

Basic usage

import { useState } from 'react';
import { YearPicker, type ISODateString } from '@kalyx/react';

function Example() {
const [year, setYear] = useState<ISODateString | null>(null);
return (
<YearPicker value={year} onChange={setYear}>
<YearPicker.Input placeholder="YYYY" />
<YearPicker.Popover>
<YearPicker.Grid />
</YearPicker.Popover>
</YearPicker>
);
}

The default displayFormat is "yyyy".

Try it live

Live Editor
function BasicYearPicker() {
  const [year, setYear] = React.useState(null);
  const headerCls = {
    header: 'kx-live-header',
    title: 'kx-live-title',
    navButton: 'kx-live-nav',
  };
  return (
    <YearPicker value={year} onChange={setYear}>
      <div className="kx-live-row">
        <YearPicker.Input className="kx-live-input" placeholder="YYYY" />
        <YearPicker.Trigger className="kx-live-trigger" aria-label="Open year picker" />
      </div>
      <YearPicker.Popover className="kx-live-popover">
        <YearPicker.Grid
          classNames={{
            ...headerCls,
            grid: 'kx-live-year-grid',
            year: 'kx-live-my-cell',
            yearSelected: 'kx-live-my-selected',
            yearCurrent: 'kx-live-my-current',
          }}
        />
      </YearPicker.Popover>
      <div className="kx-live-value">
        Selected: <code>{year ?? 'null'}</code>
      </div>
    </YearPicker>
  );
}
Result
Loading...

Parts

PartSourcePurpose
YearPicker.Rootwraps DatePicker.Rootcontrolled/uncontrolled state, displayTimezone, disabled rules
YearPicker.Input= DatePicker.Inputtext input (combobox role)
YearPicker.Trigger= DatePicker.Triggericon button
YearPicker.Popover= DatePicker.PopoverFloating UI positioning
YearPicker.Gridnew12-year decade grid with prev/next decade navigation

The grid displays the decade block containing the current year (e.g., 2016–2027 when the value is 2026). Navigate by 12 years at a time using the header buttons.

Timezone

When displayTimezone is set, year highlighting is timezone-aware. This matters when the stored UTC-ISO has been shifted to represent civil midnight in a non-UTC zone.

<YearPicker value={year} onChange={setYear} displayTimezone="America/New_York">
<YearPicker.Input />
<YearPicker.Popover>
<YearPicker.Grid />
</YearPicker.Popover>
</YearPicker>

Disabled rules

Restrict selectable years. Rules are evaluated against January 1 of each year.

Live Editor
function DisabledYearPicker() {
  const [year, setYear] = React.useState(null);
  const headerCls = {
    header: 'kx-live-header',
    title: 'kx-live-title',
    navButton: 'kx-live-nav',
  };
  return (
    <YearPicker
      value={year}
      onChange={setYear}
      disabled={[
        { before: '2020-01-01T00:00:00.000Z' },
        { after: '2030-01-01T00:00:00.000Z' },
      ]}
    >
      <div className="kx-live-row">
        <YearPicker.Input className="kx-live-input" placeholder="2020–2030" />
        <YearPicker.Trigger className="kx-live-trigger" aria-label="Open year picker" />
      </div>
      <YearPicker.Popover className="kx-live-popover">
        <YearPicker.Grid
          classNames={{
            ...headerCls,
            grid: 'kx-live-year-grid',
            year: 'kx-live-my-cell',
            yearSelected: 'kx-live-my-selected',
            yearCurrent: 'kx-live-my-current',
            yearDisabled: 'kx-live-disabled',
          }}
        />
      </YearPicker.Popover>
      <div className="kx-live-value">
        Selected: <code>{year ?? 'null'}</code>
      </div>
    </YearPicker>
  );
}
Result
Loading...
<YearPicker
value={year}
onChange={setYear}
disabled={[
{ before: '2020-01-01T00:00:00.000Z' },
{ after: '2030-01-01T00:00:00.000Z' },
]}
>
<YearPicker.Input placeholder="2020–2030" />
<YearPicker.Popover>
<YearPicker.Grid />
</YearPicker.Popover>
</YearPicker>

Uncontrolled

<form action="/api/save" method="post">
<YearPicker name="taxYear" defaultValue="2026-01-01T00:00:00.000Z">
<YearPicker.Input />
<YearPicker.Popover>
<YearPicker.Grid />
</YearPicker.Popover>
</YearPicker>
<button type="submit">Save</button>
</form>

Event callbacks

PropSignatureFires when
onChange(value: ISODateString | null) => voidA year is committed (click or input typed).
onOpenChange(isOpen: boolean) => voidThe popover opens or closes.
onCalendarNavigate(viewMonth: ISODateString) => voidThe grid navigates to a different decade.

Props

YearPicker Root accepts the same props as DatePicker.Root. Only the default displayFormat differs. See DatePicker for the full reference.

Grid classNames

<YearPicker.Grid
classNames={{
root: '',
header: '',
title: '',
navButton: '',
grid: '',
gridRow: '',
year: '',
yearSelected: '',
yearCurrent: '',
yearDisabled: '',
}}
/>