# React Month Picker: Monate, Quartale und Zeiträume

> Ein React Month Picker für Abrechnung und Reports: Wähl einen Monat, ein Quartal oder einen Zeitraum im Jahresraster, mit Presets, Limits und Bottom Sheet.

- Autor: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Veröffentlicht: 19. Okt. 2026
- Kategorie: [Komponenten-Guides](https://wingo-ui.com/de/blog/category/components)
- Lesezeit: 8 Min.
- Kanonische URL: https://wingo-ui.com/de/blog/react-month-picker
- Englische Fassung: https://wingo-ui.com/blog/react-month-picker

## Kurz gesagt

Ein React Month Picker zeigt ein Jahr als Raster aus zwölf Monaten, damit Nutzer einen Abrechnungsmonat, ein Quartal oder eine Spanne von Monaten mit ein oder zwei Klicks wählen, statt durch Tage zu blättern. Ein guter Picker liefert einen Zeitraum vom ersten Tag des ersten Monats bis zum letzten Tag des letzten Monats, bietet Quartals-Chips und Presets und öffnet auf dem Smartphone ein Bottom Sheet. Der Month Picker von Wingo UI kann das mit mode="range", Quartalen, Presets, maxMonths und einer disabled-Funktion für abgeschlossene Perioden.

Rechnungen, Gehaltsabrechnungen, Budgets und Monatsreports werden nach Monaten geführt, nicht nach Tagen. Wer dafür erst einen Tageskalender öffnen und sich zum Ersten eines Monats durchklicken muss, verliert Zeit, und Fehler sind vorprogrammiert: Jemand wählt den 14., und deine Query beginnt mitten im Monat. Ein React Month Picker zeigt das Jahr als zwölf Monate, sodass der Abrechnungsmonat oder das Quartal nur einen Klick entfernt ist. Dieser Guide zeigt, wann sich ein Month Picker lohnt, was die vorhandenen React-Optionen können und welche Props im [Month Picker](https://wingo-ui.com/components/month-picker) von Wingo UI zählen, mit Code für Abrechnungsmonate, Monatszeiträume, Quartale und abgeschlossene Buchungsperioden.

## Wann nehme ich einen Month Picker statt eines Date Pickers?

Immer dann, wenn der Tag keine Rolle spielt. Ein Abrechnungszeitraum für ein Abo, ein Umsatzreport für Q3, ein Budget für November bis Dezember, das Ablaufdatum einer Karte, ein abgeschlossener Buchungsmonat: Das alles sind Monate. Ein Month Picker bringt dir drei Dinge, die ein Tageskalender nicht hat:

1. **Ein Klick pro Monat.** Zwölf Monate passen auf einen Bildschirm, und ein Pfeil neben der Jahreszahl wechselt ins nächste Jahr.
2. **Ganze Monate im Wert.** Du bekommst nie aus Versehen einen Zeitraum, der am 14. beginnt.
3. **Quartale und mehrmonatige Zeiträume** als eigene Auswahl, die Tageskalender nicht kennen.

Brauchst du Zeiträume auf Tagesebene, etwa „die letzten 7 Tage“ in Analytics oder An- und Abreise in einem Buchungsformular, nimm stattdessen einen [React Date Range Picker](https://wingo-ui.com/de/blog/react-date-range-picker). Oft stehen beide auf derselben Report-Seite: Monate für die Periode, Tage für den Drill-down.

## Welche Optionen für einen React Month Picker gibt es?

Aktiv gepflegt werden react-datepicker, der MonthPicker von Mantine, der DatePicker von MUI X mit Monats- und Jahresansicht und der Month Picker von Wingo UI. Auf genau diese stößt du, wenn du nach einem React Month Year Picker oder einem React Month Range Picker suchst. Eine Suche nach „month picker component react“ auf npm liefert zusätzlich viele kleine Pakete für genau einen Zweck; die Tabelle beschränkt sich auf Libraries mit gepflegter Doku. Die Fakten stammen aus der Doku, dem Quellcode und dem npm-Eintrag des jeweiligen Projekts, Stand Oktober 2026.

| Option | Einzelner Monat | Monatszeitraum | Quartale | Werttyp |
|---|---|---|---|---|
| [react-datepicker](https://github.com/Hacker0x01/react-datepicker) 9.1.0, MIT | `showMonthYearPicker` | `selectsRange` mit `showMonthYearPicker` | `showQuarterYearPicker` | `Date`; ein Zeitraum ist ein Tupel `[start, end]` |
| [Mantine MonthPicker](https://mantine.dev/dates/month-picker/) (`@mantine/dates` 9.7.3, MIT) | Standard | `type="range"` | nicht in der Doku | Datums-Strings wie `'2026-07-01'` |
| [MUI X DatePicker](https://mui.com/x/react-date-pickers/date-picker/) (`@mui/x-date-pickers` 8.29.5, MIT) | `views={['month', 'year']}` | nicht in der DatePicker-Doku | nicht in der Doku | hängt vom Date-Adapter ab |
| [Month Picker von Wingo UI](https://wingo-ui.com/components/month-picker) (Pro-Komponente als Quellcode) | Standard | `mode="range"` | Chips für Q1 bis Q4 | `Date`; ein Zeitraum ist `{ from, to }` |

Ein Detail solltest du bei jedem Month Range Picker prüfen: was genau das Ende des Zeitraums ist. In react-datepicker wählt ein Klick auf einen Monat dessen Anfang (`getStartOfMonth` in seiner `month.tsx`), das Ende eines Zeitraums von Juli bis September ist also der 1. September. Eine Query mit `date <= end` verliert dann fast den ganzen September. Mantine und react-datepicker sind beide eine gute Wahl, wenn du ihre Datums-Stacks schon nutzt; Mantine hat auch Presets. Der Rest dieses Guides dreht sich um die Komponente von Wingo UI.

## Welche Props braucht ein React Month Picker?

Für einen einzelnen Monat `value` und `onValueChange`, dazu `min` und `max`, um das Jahresraster einzugrenzen. Der Wert ist der erste Tag des gewählten Monats um 00:00 Uhr.

```bash
npx wingo-ui@latest add month-picker
```

```tsx
"use client";

import { useState } from "react";
import { MonthPicker } from "@/components/ui/month-picker";

export function BillingMonth() {
  const [month, setMonth] = useState<Date | null>(new Date(2026, 8, 1));

  return (
    <MonthPicker
      label="Abrechnungsmonat"
      value={month}
      onValueChange={setMonth}
      min={new Date(2024, 0, 1)}
      max={new Date()}
      presets
      clearable
      name="month"
    />
  );
}
```

Ein Klick auf das Feld öffnet ein Panel mit dem Jahr oben und seinen zwölf Monaten in vier Reihen zu je drei. Der aktuelle Monat trägt einen kleinen Punkt. Die Pfeile blättern ein Jahr weiter, und ein Klick auf das Jahr öffnet ein Raster aus zwölf Jahren für große Sprünge. Ein Klick auf einen Monat setzt ihn und schließt das Panel (`closeOnSelect` ist standardmäßig `true`). Mit `presets` kommen im Einzelmodus „This month“ und „Last month“ dazu. Mit `name` schickt ein verstecktes Input-Feld `month=2026-09` mit deinem Formular ab.

Die Props, die du danach am häufigsten änderst, sind `size` (`sm`, `md`, `lg`), `variant` (`soft`, `outline`, `underline`, `ghost`), `monthFormat` (`short` oder `long` für kurze oder lange Monatsnamen in den Zellen) und `tone` für den gewählten Monat.

> Live-Demo (Month Picker): Öffne das Feld und wähl einen Monat: Das Panel schließt sich, und die Umsatzkarte zeigt diesen Monat neben den zwei davor. Nutz die Jahrespfeile, oder klick auf das Jahr, um zwölf Jahre auf einmal zu springen. Probier sie unter [Month Picker](https://wingo-ui.com/components/month-picker) aus und installier sie mit `npx wingo-ui@latest add month-picker`.

## Wie wähle ich einen Zeitraum aus Monaten?

Setz `mode="range"`. Der erste Klick setzt den ersten Monat, beim Bewegen des Zeigers siehst du den Bereich als Vorschau, und der zweite Klick setzt den letzten Monat. Liegt der zweite Monat vor dem ersten, werden beide getauscht. `onValueChange` bekommt nur vollständige Zeiträume, ein halb gewählter Zeitraum erreicht deine Report-Query also nie.

Der Wert reicht vom ersten Tag des ersten Monats bis zum letzten Tag des letzten, beide um 00:00 Uhr Ortszeit. Juli bis September ist damit `{ from: Jul 1, to: Sep 30 }`, und das liest sich in einer Zusammenfassung richtig. Bei einer Timestamp-Spalte vergleichst du mit dem Tag nach `to`, sonst fallen Zeilen vom 30. September nach Mitternacht heraus:

```tsx
"use client";

import { useState } from "react";
import { MonthPicker, type DateRange } from "@/components/ui/month-picker";
import { toISODate } from "@/lib/date-utils";

export function SalesPeriod({ onLoad }: { onLoad: (from: string, until: string) => void }) {
  const [period, setPeriod] = useState<DateRange>({ from: new Date(2026, 6, 1), to: new Date(2026, 8, 30) });

  return (
    <MonthPicker
      mode="range"
      label="Zeitraum"
      presets
      maxMonths={12}
      max={new Date()}
      value={period}
      onValueChange={(next) => {
        setPeriod(next);
        if (!next.from || !next.to) return;
        // exklusives ende: der erste tag nach dem zeitraum
        const until = new Date(next.to.getFullYear(), next.to.getMonth(), next.to.getDate() + 1);
        onLoad(toISODate(next.from), toISODate(until));
      }}
    />
  );
}
```

`toISODate` schreibt das lokale Kalenderdatum (`2026-07-01`) und rechnet nie nach UTC um, deshalb verschiebt sich der Tag nicht mit der Zeitzone. In einem Formular schickt `name="period"` die Felder `period.from` und `period.to` als dieselben lokalen ISO-Daten ab.

`presets` ergänzt im Range-Modus „This month“, „Last month“, die letzten 3, 6 und 12 Monate, „This year“ und „Last year“, jeweils auf `min` und `max` begrenzt. Ein Preset, das `maxMonths` überschreiten oder einen gesperrten Monat berühren würde, wird deaktiviert. Du kannst auch eine eigene Liste aus `{ label, value }` übergeben, wobei `value` ein `Date`, ein `DateRange` oder eine Funktion ist, die eines davon liefert. `maxMonths` begrenzt die Spanne: Während du das Ende wählst, lassen sich Monate jenseits des Limits nicht anklicken.

> Live-Demo (Month Picker): Auf Range-Modus mit Presets umgestellt: Wähl Juli, dann September, oder drück Q3 oder „Last 3 months“. Die Zusammenfassung unter dem Raster zählt die Monate, und die Gesamtsumme rechnet sie zusammen. Probier sie unter [Month Picker](https://wingo-ui.com/components/month-picker) aus und installier sie mit `npx wingo-ui@latest add month-picker`.

## Wie wähle ich ein Quartal?

Im Range-Modus zeigt das Panel unter dem Raster Chips für Q1 bis Q4, und jeder wählt seine drei Monate auf einmal. Im Einzelmodus sind sie ausgeblendet; mit `quarters` blendest du sie ein, und ein Quartal wählt dann seinen ersten Monat. Der Buchstabe für das Quartal richtet sich nach der Locale: Q auf Englisch, T auf Rumänisch, Französisch, Spanisch, Italienisch und Portugiesisch.

Für den Code gibt es zwei exportierte Helfer. `quarterRange(year, quarter)` liefert das Quartal als Zeitraum, und `getQuarter(date)` liefert 1 bis 4. Zusammen mit `maxMonths={3}` und `formatValue` zeigt das Feld „Q3 2026“ für ein volles Quartal und fällt bei allem Kürzeren auf den Monatszeitraum zurück:

```tsx
"use client";

import { useState } from "react";
import { MonthPicker, getQuarter, quarterRange, type DateRange } from "@/components/ui/month-picker";
import { formatDateRange } from "@/lib/date-utils";

// ein volles quartal beginnt im jan, apr, jul oder okt und umfasst drei monate
function isFullQuarter(range: DateRange) {
  if (!range.from || !range.to) return false;
  const months =
    (range.to.getFullYear() - range.from.getFullYear()) * 12 + range.to.getMonth() - range.from.getMonth() + 1;
  return range.from.getMonth() % 3 === 0 && months === 3;
}

export function FiscalQuarter() {
  const [period, setPeriod] = useState<DateRange>(quarterRange(2026, 3));

  return (
    <MonthPicker
      mode="range"
      label="Geschäftsquartal"
      value={period}
      onValueChange={setPeriod}
      maxMonths={3}
      formatValue={(value, locale) =>
        isFullQuarter(value) && value.from
          ? `Q${getQuarter(value.from)} ${value.from.getFullYear()}`
          : formatDateRange(value, locale, { month: "short", year: "numeric" })
      }
    />
  );
}
```

Ohne diese Prüfung würde auch eine Auswahl von Juli bis August „Q3 2026“ anzeigen und verbergen, dass der Zeitraum zu kurz ist. Das Beispiel „Fiscal quarter“ in der Registry-Demo nutzt dieselbe Prüfung, um in seinem Hinweis zwischen quartalsweiser und monatlicher Steuermeldung umzuschalten.

## Wie sperre ich abgeschlossene Monate?

Übergib eine Funktion an `disabled`. Sie bekommt den ersten Tag jedes Monats, und Monate, für die sie `true` liefert, lassen sich nicht wählen. Das passt zu abgeschlossenen Buchungsperioden, Monate vor Vertragsbeginn oder Monate ohne Daten:

```tsx
const closedBefore = new Date(2026, 6, 1);

<MonthPicker
  label="Buchungsperiode"
  value={month}
  onValueChange={setMonth}
  disabled={(m) => m < closedBefore}
  max={new Date()}
  description="Abgeschlossene Monate kannst du nicht mehr bearbeiten."
/>
```

Im Range-Modus kann ein Bereich keinen gesperrten Monat überspannen. Ein kontrollierter `value` außerhalb von `min` und `max` oder auf einem gesperrten Monat wird trotzdem angezeigt, und das Feld wird als ungültig markiert. So bleibt das Problem sichtbar, und der Wert wird nicht stillschweigend geändert. `disabled={true}` sperrt das ganze Feld, wie bei jedem Input.

## Was ist mit Ablaufdaten von Karten und eigenen Formaten?

Den Text im Trigger formatiert `Intl.DateTimeFormat`: „September 2026“ im Einzelmodus, Zeiträume über `formatRange`, sodass Juli bis September 2026 das gemeinsame Jahr nur einmal zeigt. `displayFormat` ändert die Bestandteile, und `formatValue` deckt Formate ab, die Intl nicht schreibt, etwa das `09/28` auf einer Zahlungskarte:

```tsx
<MonthPicker
  label="Ablaufdatum"
  value={expiry}
  onValueChange={setExpiry}
  min={new Date()}
  formatValue={(value) => (value ? `${String(value.getMonth() + 1).padStart(2, "0")}/${String(value.getFullYear()).slice(2)}` : "")}
  placeholder="MM/JJ"
/>
```

`locale` bestimmt die Monatsnamen, den Text im Trigger und den Quartalsbuchstaben. Ohne `locale` gilt der nächste `LocaleProvider`, sonst die Sprache der Seite.

## Ist er per Tastatur bedienbar?

Ja. Enter, Leertaste oder Pfeil nach unten auf dem Feld öffnen das Panel, mit dem Fokus auf dem gewählten Monat, sonst auf dem aktuellen. Die Pfeiltasten bewegen den Fokus um einen Monat, Pfeil hoch und runter um drei (eine Reihe), Home und End springen an die Enden der Reihe, und PageUp und PageDown wechseln das Jahr. Enter oder Leertaste wählt. Escape geht Schritt für Schritt zurück: Erst verlässt es das Jahresraster, dann verwirft es einen halb gewählten Zeitraum, dann schließt es das Panel und gibt den Fokus ans Feld zurück. Das Raster nutzt `role="grid"`, gewählte Zellen sind als ausgewählt markiert, jeder Monat hat ein vollständiges Label wie „September 2026“, der aktuelle Monat trägt `aria-current="date"`, und jede Auswahl wird über eine Live Region im Modus „polite“ angesagt. Bei reduzierter Bewegung ersetzt ein Fade den Slide beim Jahreswechsel.

## Wie funktioniert er auf dem Smartphone?

Unter 768px öffnet das Feld statt eines Popovers ein Bottom Sheet: das Label als Titel, Presets als Reihe von Chips, 48px große Monatszellen und die Quartals-Chips. Im Einzelmodus übernimmt ein Tippen den Monat und schließt das Sheet. Im Range-Modus bleibt ein Entwurf im Sheet, bis du Apply drückst, und Cancel oder das Wegziehen des Sheets behalten den alten Wert. Mit `responsive={false}` bleibt es überall beim Popover. Das Sheet-Muster selbst erklärt der [Guide zum React Bottom Sheet](https://wingo-ui.com/de/blog/react-bottom-sheet-for-the-web), und es baut auf dem kostenlosen [Drawer](https://wingo-ui.com/components/drawer) auf.

Für einen Dashboard-Filter, der immer sichtbar sein soll, rendert `inline` das Panel direkt auf der Seite ohne Trigger, und `MonthPickerPanel` exportiert das Raster allein für eigene Layouts.

## Wie füge ich ihn meinem Projekt hinzu?

Führ `npx wingo-ui@latest add month-picker` aus. Die CLI kopiert den Quellcode nach `components/ui`, ergänzt Field, Drawer, Button und die Datums-Helfer, die er importiert, und installiert `@radix-ui/react-popover`, `motion` und `lucide-react`. Der Month Picker ist eine Pro-Komponente ([Preise](https://wingo-ui.com/de/pricing)), genau wie der [Date Range Picker](https://wingo-ui.com/components/date-range-picker) für Zeiträume auf Tagesebene. Zeiteingaben behandelt der [Guide zum React Time Picker](https://wingo-ui.com/de/blog/react-time-picker), und der [Guide zu React-Formular-Komponenten](https://wingo-ui.com/de/blog/react-form-components-guide) wendet dieselben Regeln für Tastatur, Smartphone und Labels auf jedes Feld an. Weitere Deep Dives zu einzelnen Komponenten findest du in der [Kategorie Komponenten-Guides](https://wingo-ui.com/de/blog/category/components).

## Komponenten in diesem Artikel

- [Month Picker](https://wingo-ui.com/components/month-picker) (Pro): A React month picker for one month or a range from a year grid, with quarters and presets, for reports, billing periods and expiry dates. Installation: `npx wingo-ui@latest add month-picker`
- [Date Range Picker](https://wingo-ui.com/components/date-range-picker) (Pro): A React date range picker for reports and bookings: two months with presets on desktop, a one-month bottom sheet on phones. Installation: `npx wingo-ui@latest add date-range-picker`

## Häufige Fragen

### Wie baue ich einen Monats- und Jahres-Picker in React?

Rendere einen Jahreskopf mit Pfeilen und ein Raster aus zwölf Monaten, und speichere den ersten Tag des gewählten Monats. react-datepicker macht das mit showMonthYearPicker, MUI X mit views={['month', 'year']}, und der Month Picker von Wingo UI ist standardmäßig ein Monats- und Jahres-Picker.

### Wie wähle ich in React einen Zeitraum aus Monaten?

Nimm einen Picker mit Range-Modus: Der erste Klick setzt den ersten Monat, der zweite den letzten. Im Month Picker von Wingo UI ist das mode="range", und der Wert reicht vom ersten Tag des ersten Monats bis zum letzten Tag des letzten Monats.

### Hat shadcn/ui einen Month Picker?

Nein. Stand Oktober 2026 enthält die Komponentenliste von shadcn/ui einen Calendar und einen Date Picker, aber keinen Month Picker. Du baust also selbst einen oder installierst eine Quellcode-Komponente wie den Month Picker von Wingo UI.

### Wie wähle ich ein Quartal statt eines Monats?

Zeig unter dem Monatsraster Chips für Q1 bis Q4, die jeweils drei Monate auf einmal wählen. Der Month Picker von Wingo UI zeigt sie im Range-Modus standardmäßig, und quarterRange(2026, 3) liefert dir im Code den 1. Juli bis 30. September 2026.

### Ist der Month Picker von Wingo UI kostenlos?

Nein. Month Picker und Date Range Picker sind Pro-Komponenten. Der Drawer, den er für das Bottom Sheet auf dem Smartphone nutzt, und das Field drumherum sind kostenlos.

---

Quelle: https://wingo-ui.com/de/blog/react-month-picker
