Month Picker
Accessible, unstyled grid of the twelve months of a year with year paging.
| Package | @vizejs/ui/month-picker |
| Maturity | stable |
| Own the source | vize lib pull month-picker |
| Requires | calendar, controllable-state, id, locale |
| Aliases | month grid, month input, billing month, year view |
| Covers | HTML input type=month, WAI-ARIA grid, MUI MonthCalendar |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { MonthPicker } from "@vizejs/ui/month-picker";
Or copy the source into your project with vize lib pull month-picker (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Month picker bound with v-model, a fixed SSR-safe today, and a selectable range. -->
<script setup lang="ts">
import { ref } from "vue";
import { formatIsoYearMonth, MonthPicker } from "@vizejs/ui/month-picker";
import type { PlainYearMonth } from "@vizejs/ui/month-picker";
const billing = ref<PlainYearMonth | null>({ year: 2026, month: 10 });
</script>
<template>
<div>
<MonthPicker
v-model="billing"
name="billing-month"
locale="en-US"
aria-label="Billing month"
:today="{ year: 2026, month: 10, day: 1 }"
:min="{ year: 2025, month: 1 }"
:max="{ year: 2027, month: 12 }"
/>
<output>{{ billing ? formatIsoYearMonth(billing) : "No month" }}</output>
</div>
</template>
API
MonthPicker
Source: src/families/date-time/month-picker/month-picker.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string | null | undefined |
undefined |
Consumer-owned base id; nullish values use a deterministic fallback. |
modelValue |
PlainYearMonth | null | undefined |
undefined |
Controlled month; undefined selects uncontrolled mode and null clears. |
defaultValue |
PlainYearMonth | null | undefined |
null |
Initial uncontrolled month. |
min |
PlainYearMonth | null | undefined |
undefined |
Earliest selectable month. |
max |
PlainYearMonth | null | undefined |
undefined |
Latest selectable month. |
isMonthUnavailable |
((value: PlainYearMonth) => boolean) | undefined |
undefined |
Predicate for months that stay focusable but cannot be selected. |
locale |
string | undefined |
undefined |
BCP 47 locale; defaults to the nearest LocaleProvider. |
dir |
"ltr" | "rtl" | undefined |
undefined |
Text direction; defaults to the nearest LocaleProvider. |
calendar |
string | undefined |
undefined |
Intl calendar used for display labels only. |
numberingSystem |
string | undefined |
undefined |
Intl numbering system for labels. |
monthFormat |
MonthPickerFormat | undefined |
"short" |
Month label width. |
columns |
number | undefined |
3 |
Months per grid row. |
today |
PlainDate | null | undefined |
undefined |
Explicit current date; the SSR-safe way to mark the current month. |
now |
DateTimeNow | undefined |
undefined |
Injectable clock evaluated during setup on server and client. |
timeZone |
string | undefined |
undefined |
IANA time zone used with now and the post-mount host clock. |
disabled |
boolean | undefined |
false |
Disable navigation, focus, and selection. |
readOnly |
boolean | undefined |
false |
Allow navigation while blocking selection. |
name |
string | undefined |
undefined |
Hidden input name that submits YYYY-MM. |
previousLabel |
string | undefined |
"Previous year" |
Accessible name of the previous-year control. |
nextLabel |
string | undefined |
"Next year" |
Accessible name of the next-year control. |
ariaLabel |
string | undefined |
undefined |
Accessible name for the picker group. |
ariaLabelledby |
string | undefined |
undefined |
Ids that label the picker group; defaults to the year heading. |
ariaDescribedby |
string | undefined |
undefined |
Ids that describe the picker group. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue |
[value: PlainYearMonth | null] |
Fired when the picker requests a new controlled month. |
change |
[ value: PlainYearMonth | null, previous: PlainYearMonth | null, nativeEvent: Event | null, ] |
Fired after a distinct selection with the previous month and triggering event. |
update:focusedMonth |
[value: PlainYearMonth] |
Fired when keyboard or navigation moves the focused month. |
Slots
| Slot | Slot props | Description |
|---|---|---|
heading |
MonthPickerSlotState |
Year heading content. Receives picker state. |
previous |
MonthPickerSlotState |
Previous-year control content. |
next |
MonthPickerSlotState |
Next-year control content. |
cell |
MonthPickerCellState |
Month cell content. Receives the cell state; defaults to the localized month. |
Behavior
Normative state x input -> outcome table for month-picker.vue
(@vizejs/ui/month-picker), a grid of the twelve months of one year built on
the shared linear period grid (period-grid-runtime.ts). Every row is proven
by the named test.
| # | State | Input | Outcome | Proven by |
|---|---|---|---|---|
| MP1 | known year | render | a role="group" labelled by the live year heading holds a <table role="grid"> of columns-wide rows; one roving tabindex=0 month; the current month has aria-current="date"; name submits YYYY-MM |
renders a labelled month grid with roving focus and the current month |
| MP2 | focused | Arrow / Home / End / PageUp / PageDown | ±1 month, ±1 row, row edges, ±1 year, Shift ±10 years; Enter/Space selects and emits update:modelValue and change |
keyboard moves by month, row, row edge, year, and decade; activation selects |
| MP3 | RTL / bounds | arrows / activation / previous–next | RTL flips horizontal arrows; focus clamps to min/max; out-of-range months are disabled; unavailable months stay focusable and unselectable; year controls disable at bounds |
RTL, min/max bounds, unavailability, and year paging controls |
| MP4 | no value, no clock | mount / expose | pending until mount, then the host clock picks the year; setValue, navigate, and focus are exposed |
pending until mount without a clock, exposed API, and unit helpers |
| MP5 | SSR/hydration | isolated requests | byte-identical markup and silent hydration with an explicit today |
renders byte-identical month picker markup across isolated SSR requests, hydrates month picker markup without mismatches |
SSR determinism follows calendar.behavior.md: pass today or now +
timeZone, or a value, to render a complete grid on the server.
Parts: root, header, previous, heading, next, grid, row, cell, month.