Vize

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.