Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Inputs

Month Picker

Inputs

Month Picker React component for Tailwind CSS v4

Mention InputMulti Select
Live props
State
mode
disabled
readOnly
required
invalid
defaultOpen
Content
label
description
error
placeholder
presets
locale
sheetTitle
Appearance
size
variant
radius
tone
color
monthFormat
Behavior
quarters
maxMonthsNo limit
clearable
closeOnSelect
name
Layout
inline
responsive
side
align
Code
import { MonthPicker } from "@/components/ui/month-picker"

<MonthPicker />
  1. Home
  2. Components
  3. Inputs
  4. Month Picker
Pro

Month Picker React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

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.

Click the field to open a panel under it with the year at the top and its twelve months in four rows of three; the current month carries a small dot. The arrows beside the year move one year (the grid slides that way), and the year itself opens a grid of twelve years for long jumps (its arrows move twelve years). In single mode a click on a month sets it, the fill glides to it and the panel closes. In range mode the first click sets the first month, moving the pointer previews the band, and the second click sets the last month (a month before the first swaps them); months past maxMonths or beyond a disabled month cannot be picked while the band is open. The range commits on the second click and the panel stays open until Escape, Enter or a click outside, with a summary like "Jul - Sep 2026 · 3 months" under the grid. Quarter chips (Q1 to Q4, T1 to T4 in French or Spanish) pick three months at once, or the quarter's first month in single mode, and presets (This month, Last 3 months, This year...) sit in a column on the left, clamped to min and max. The clear button empties the field. Keyboard: Enter, Space or ArrowDown on the field open the panel with focus on the selected (else the current) month; arrows move one month, Up / Down three, Home / End the row's ends, PageUp / PageDown a year, Enter or Space pick, and Enter on the last month of a range closes the panel. Escape first leaves the years grid, then drops a half-picked range, then closes and returns focus to the field. On a phone the field opens a bottom sheet with the title, the presets as a row of chips, 48px month cells and the quarters; single mode commits on tap and closes, range mode keeps a draft in the sheet until Apply, and Cancel or dragging the sheet away keeps the old value. name submits YYYY-MM, or name.from and name.to as ISO dates.

Month Picker is a Pro React component in the React input components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add month-picker, together with Button, Drawer, Field, Date utils, Relative time and 6 more and the npm packages @radix-ui/react-popover, motion and lucide-react.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add month-picker

Month Picker is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import MonthPicker from @/components/ui/month-picker.

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

export function SalesReport() {
  const [period, setPeriod] = useState<DateRange>({ from: new Date(2026, 6, 1), to: new Date(2026, 8, 30) });
  const [month, setMonth] = useState<Date | null>(new Date(2026, 8, 1));
  return (
    <div className="flex flex-col gap-4">
      <MonthPicker
        mode="range"
        label="Period"
        presets
        max={new Date()}
        value={period}
        onValueChange={setPeriod}
        name="period"
        labels={{ apply: "Done" }}
      />
      <MonthPicker label="Billing month" value={month} onValueChange={setMonth} min={new Date(2024, 0, 1)} max={new Date()} />
    </div>
  );
}

Props

  • modedefault "single"
    "single" | "range"

    single picks one month (value: its first day at 00:00); range picks a span of months (value: from the first day of the first month to the last day of the last one, ready for a query).

  • value
    Date | null (single) | DateRange (range)

    The month or range, controlled. A date in the middle of a month shows as its month and is not rewritten; a value outside min / max or on a disabled month shows as given and flags the field invalid.

  • defaultValuedefault "null / { from: null, to: null }"
    Date | null (single) | DateRange (range)

    The starting value while uncontrolled. A form reset returns to it.

  • onValueChange
    (value: MonthPickerValue<mode>) => void

    Called with the new month (single) or the complete range (range: on the second click, a quarter or a preset; on phones on Apply). A half-picked range never reaches it. null or { from: null, to: null } when cleared.

  • labeldefault ""
    ReactNode

    The field label. Plain text also titles the phone sheet and names the panel.

  • descriptiondefault ""
    ReactNode

    Helper text under the field, linked with aria-describedby.

  • errordefault ""
    ReactNode

    Replaces the description with the error and marks the field invalid.

  • placeholderdefault ""
    string

    The empty trigger text. Empty uses "Pick a month" (single) or "Pick months" (range).

  • presetsdefault false
    boolean | MonthPreset[]

    true: This month, Last month, Last 3 / 6 / 12 months, This year and Last year in range mode (This month and Last month in single), clamped to min / max and off when they break maxMonths or touch a disabled month. Or your own list of { label, value: Date | DateRange | () => Date | DateRange, key? }. A column on the left from 640px, a chip rail on phones.

  • localedefault ""
    string

    Month names, the trigger text and the quarter letter (Q in English, T in Romanian, French, Spanish, Italian, Portuguese). Empty uses the nearest LocaleProvider, else the page's language.

  • sheetTitledefault ""
    string

    The phone sheet title when the label is not plain text. Empty uses labels.dialog ("Choose month").

  • displayFormatdefault "{ month: "long", year: "numeric" } (single), { month: "short", year: "numeric" } (range)"
    Intl.DateTimeFormatOptions

    The trigger text through Intl: "septembrie 2026"; ranges through formatRange, the shared year folded ("iul. - sept. 2026", across years "nov. 2025 - feb. 2026").

  • formatValue
    (value: MonthPickerValue<mode>, locale: string) => string

    Your own trigger text, for formats Intl does not write, like a card expiry "09/28".

  • icondefault "<CalendarDays />"
    ReactNode

    The leading icon of the trigger.

  • labelsdefault "DEFAULT_MONTH_PICKER_LABELS"
    Partial<MonthPickerLabels>

    Every string: previousYear, nextYear, previousYears, nextYears, chooseYear, quarter(q), quarters, presets, the preset names (thisMonth, lastMonth, last3Months, last6Months, last12Months, thisYear, lastYear), months(count) for the summary, pickStart, pickEnd, apply, cancel, clear, dialog.

  • sizedefault "md"
    "sm" | "md" | "lg"

    The field height: 32 / 40 / 48px (44px on touch for md), matching inputs and selects.

  • variantdefault "soft"
    "soft" | "outline" | "underline" | "ghost"

    The field box: a soft fill, a hairline outline, a bottom line, or nothing until hover.

  • radiusdefault "md"
    "none" | "sm" | "md" | "lg" | "full"

    The field corners, per size like the button.

  • tonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    The selected months, the range band, the quarter chips and Apply.

  • colordefault ""
    string

    Any CSS color for the selection; replaces the tone.

  • monthFormatdefault "short"
    "short" | "long"

    The month cells: "Sept." or "Septembrie".

  • classNames
    Partial<Record<MonthPickerSlot, string>>

    Classes per part: field, label, description, error, box, trigger, icon, value, clear, chevron, content, panel, header, title, nav, grid, month, years, year, quarters, quarter, presets, preset, footer, summary.

  • min
    Date

    The first month that can be picked; earlier months are disabled and the year arrows stop there.

  • max
    Date

    The last month that can be picked. A max before min is ignored.

  • quartersdefault false
    boolean

    The Q1 to Q4 chips under the grid. Left unset they show in range mode (three months at once) and hide in single mode, where true adds them and a quarter picks its first month.

  • maxMonthsdefault 0
    number

    The longest range in months; while you pick the end, months farther away are disabled. 0 = no limit.

  • clearabledefault false
    boolean

    A clear button inside the field while it has a value.

  • closeOnSelectdefault true
    boolean

    Single mode closes after a pick. Range mode always stays open until Escape, Enter or a click outside.

  • now
    Date | number

    What this month means: the current-month dot and the presets. Left out, the live clock (useNow); pin it for screenshots.

  • namedefault ""
    string

    Hidden inputs for forms: single submits YYYY-MM, range submits name.from and name.to as ISO dates.

  • disableddefault false
    boolean or (month: Date) => boolean

    true dims the whole field and never opens it. A test disables single months instead (closed accounting periods): it gets each month's first day.

  • readOnlydefault false
    boolean

    The value stays visible but the panel never opens.

  • requireddefault false
    boolean

    The mark after the label and aria-required on the trigger.

  • invaliddefault false
    boolean

    The invalid look and aria-invalid without a message.

  • open
    boolean

    The panel, controlled. Pair it with onOpenChange.

  • defaultOpendefault false
    boolean

    Opens the panel on mount while uncontrolled.

  • onOpenChange
    (open: boolean) => void

    Called when the panel or sheet opens or closes.

  • inlinedefault false
    boolean

    The panel sits on the page without a trigger (a raised surface with the grid, quarters, presets and the range summary).

  • responsivedefault true
    boolean

    A bottom sheet below 768px instead of the popover.

  • sidedefault "bottom"
    "top" | "right" | "bottom" | "left"

    Where the popover opens; it flips when there is no room.

  • aligndefault "start"
    "start" | "center" | "end"

    How the popover lines up with the field.

  • container
    HTMLElement | null

    The portal target of the popover and the sheet (default document.body).

  • id
    string

    The trigger's id; the label points at it.

  • fieldProps
    Partial<FieldProps>

    Anything else for the Field wrapper: optional, labelAction, orientation, hideLabel.

  • ref
    Ref<HTMLButtonElement> (a useRef object or an (el) => void callback)

    The trigger button (a regular prop in React 19).

Parts

  • MonthPickerPanel

    component

    The grid alone on a raised panel, for inline use and composition: mode, value / defaultValue / onValueChange, min, max, disabled, quarters, presets, maxMonths, monthFormat, tone, color, now, locale, labels, classNames, autoFocus. Props: MonthPickerPanelProps<M>.

  • DEFAULT_MONTH_PICKER_LABELS

    constant

    The English strings; spread them to translate a few. Type: MonthPickerLabels.

  • startOfMonthDate

    function

    The first / last day of the date's month at 00:00. Signature: (date: Date) => Date.

  • endOfMonthDate

    function

    The first / last day of the date's month at 00:00. Signature: (date: Date) => Date.

  • getQuarter

    function

    The quarter a date falls in. Signature: (date: Date) => 1 | 2 | 3 | 4.

  • quarterRange

    function

    The quarter as a range: its first day to its last day (Q3 2026: Jul 1, 2026 to Sep 30, 2026). Signature: (year: number, quarter: number) => { from: Date; to: Date }.

Examples

Billing month

Single month between Jan 2024 and this month, with presets and clear.

Card expiry

A custom formatValue writes "09/28"; months before now are closed.

Fiscal quarter

Range mode with quarter chips and maxMonths 3, shown as Q3 2026.

Inline budget planner

The panel on the page with custom presets, brand tone and a 6 month cap.

Closed accounting periods

A disabled test closes every month before July 2026.

en-US vs fr-FR

Long month names and the quarter letter (Q or T) follow the locale; the range spans two years.

Tones and sizes

sm outline brand, md success range, lg custom color.

Dependencies

@radix-ui/react-popovermotionlucide-reactbuttondrawerfielddate-utilsrelative-timeuse-controllable-stateuse-localeuse-media-querymotiontonesui-config

Files

  • components/ui/month-picker.tsx

FAQ

Is the React Month Picker component free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Month Picker?

Run npx wingo-ui@latest add month-picker. It also adds the Wingo UI items it builds on (Button, Drawer, Field, Date utils, Relative time and 6 more) and the npm packages @radix-ui/react-popover, motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/month-picker.

How does Month Picker behave on phones?

A bottom sheet below 768px instead of the popover. The responsive prop controls this and is on by default.

See also

Date PickerDate Range PickerCalendarSegmented Control

More React input components

Multi SelectNumber InputPassword InputPhone InputRadio GroupRating
All 40 React input components

Articles using this component

  • React Month Picker: Months, Quarters and Ranges

    Component guides · Oct 19, 2026 · 8 min read

PreviousMention InputNext Multi Select

Sales report

Northwind Software Inc. · Portland, OR

  • Jul$38,400
  • Aug$33,900
  • Sep$41,950
Total · 1 month$41,950
period=2026-09empty: “Pick a month”phone: sheet “Report month” · bottom / start · closes on select