Skip to main content
On this page

Lets users input a month and year using a calendar popup or free text.

When to use​

  • When asking the user to select a month where the specific day is not meaningful: for example, a billing period or usage period
  • When it is useful for the user to browse nearby months for context, or to see which months are available
  • When months could span far into the past or future, and scrolling through individual days would be inefficient

When not to use​

  • When the user needs an exact date - use Datepicker Field instead
  • When choosing from a small, fixed set of months - use Select Field or Radio instead. Browsing a calendar adds interaction cost when there is nothing to browse
  • To indicate that a field is unavailable - the disabled state is not supported. Use Summary List to show values that cannot be edited, or conditionally hide the field if it is not relevant in the current context.

Anatomy​

  1. Field: based on Text Field, contains label, hint text, input, and error state
  2. Calendar button: opens the calendar popover
  3. Year controls: moves the year forwards and backwards
  4. Selected month
  5. Today's month
  6. This month button: selects today's month and closes the calendar popover
  7. Clear button: clears the current entry and closes the calendar popover
  8. Calendar popover: always a fixed width, always aligned to the left edge of the field

Basic usage​

Loading...
Edit live code
<MonthpickerField
  id="month"
  label="Month"
/>

Validation​

MonthpickerField uses the mm/yyyy format. The input is a free-text field, so always validate on submit. Use getMonthpickerDate to parse and validate the string: it returns a Date pinned to the 1st of the selected month, or null if invalid.

Loading...
Edit live code
() => {

  const Example = () => {
    const [errors, setErrors] = useState([]);

    const onSubmit = (e) => {
      e.preventDefault();
      const newErrors = [];

      const { monthpicker } = Object.fromEntries(new FormData(e.currentTarget));

      const monthpickerDate = getMonthpickerDate(monthpicker)

      if (!monthpickerDate) {
        newErrors.push({ id: 'monthpicker', message: 'Enter a valid month in the format mm/yyyy'});
      }

      setErrors(newErrors);

      if (!newErrors.length) {
        alert('success');
      }
    }

    return (
      <Form errors={errors} onSubmit={onSubmit}>
        <FormFields>
          <MonthpickerField
            id="monthpicker"
            name="monthpicker"
            label="Monthpicker example"
          />
        </FormFields>
        <FormActions>
          <PrimaryCTAButton type="submit">Validate month</PrimaryCTAButton>
        </FormActions>
      </Form>
    );
  }

  return <Example />;
}

Unavailable months​

Use isMonthUnavailable to mark months as unavailable in the popover. This does not prevent manual entry, so always validate on submit too.

Loading...
Edit live code
<MonthpickerField
  id="month"
  label="Month"
  hint="Must be a future month"
  isMonthUnavailable={date => date.getTime() < Date.now()}
/>

Month ranges​

Use two Monthpicker Fields side by side. Set months before the start month as unavailable in the end field, and reset the end month when the start changes.

Align the two fields to the bottom of their container so inputs stay aligned when one has hint text or an error.

Loading...
Edit live code
() => { // ignore - for live code example only.
  const Example = () => {
    const [startMonth, setStartMonth] = useState('');
    const [endMonth, setEndMonth] = useState('');

    const isMonthUnavailable = (date) => {
      if (!startMonth) {
        return false;
      }
      const [m, y] = startMonth.split('/');
      const start = new Date(y + '/' + m + '/01');
      return date.getTime() < start.getTime();
    };

    return (
      <div style={{ display: 'flex', gap: '1.2rem', alignItems: 'flex-end' }}>
        <MonthpickerField
          id="start-month"
          name="start-month"
          label="Start month"
          hint="After this month"
          value={startMonth}
          onChange={value => {
            setStartMonth(value);
            setEndMonth('');
          }}
        />
        <MonthpickerField
          id="end-month"
          name="end-month"
          label="End month"
          value={endMonth}
          onChange={setEndMonth}
          isMonthUnavailable={isMonthUnavailable}
        />
      </div>
    );
  }

  return <Example />;
}

Common form features​

Monthpicker Field shares validation, hint text, required/optional marking, error display, and field width behaviour with other form fields. See the forms pattern guide for full guidance.

Content​

Labels​

Use a noun that describes the month, not a sentence fragment ("Billing month", not "Billing for"). Keep labels to two or three words.

Hints​

Use hints to describe a constraint on the month. The field already shows the format as placeholder text, so do not repeat it as a hint.

OverlayProvider​

Monthpicker Field uses React Aria. Wrap your app in OverlayProvider so the page behind the calendar popover is hidden from screen readers while it is open.