---
title: "React Calendar Quarter and Year View Example | Mobiscroll"
component: "Calendar"
framework: "React"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/react/calendar/quarter-year-view"
---

# Quarter and Year View - React Calendar demo

A React date picker with switchable quarter and year views — select dates across a 3-month grid or all 12 months at once. For React and Next.JS.

- [Live demo](https://demo.mobiscroll.com/react/calendar/quarter-year-view)
- [View React JS code on GitHub](https://github.com/acidb/mobiscroll-demos-react/tree/main/src/demos/datepicker/calendar/quarter-year-view)
- [View React TS code on GitHub](https://github.com/acidb/mobiscroll-demos-react-ts/tree/main/src/demos/datepicker/calendar/quarter-year-view)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/javascript/calendar/quarter-year-view.md), [jQuery](https://demo.mobiscroll.com/jquery/calendar/quarter-year-view.md), [Angular](https://demo.mobiscroll.com/angular/calendar/quarter-year-view.md), [Vue](https://demo.mobiscroll.com/vue/calendar/quarter-year-view.md)

## Description

Create a calendar with quarter views that can be navigated quarter by quarter or switch to a year view on the fly. Show any relevant information with [labels or add background colors](https://demo.mobiscroll.com/react/calendar/dots-colors-labels) to days along with [week counters](https://demo.mobiscroll.com/react/calendar/month-change-direction-week-numbers-outer-days).

## What this demo shows

- A date picker calendar with a segmented control for switching between quarter and year views.
- **Header and segmented control:** The centered segmented control provides `Q1`, `Q2`, `Q3`, `Q4`, and `Year` options. It sits between the year quick navigation label on the left and the previous and next navigation arrows on the right.
- **Quarter view:** The current quarter is selected by default, displaying its three months in a compact multi-month grid.
- **Year view:** Selecting `Year` displays all 12 months of the current year in a single scrollable layout.
- **Month calendars:** Each month has a fixed weekday header with abbreviated day names from Sunday through Saturday and a date grid below it. On the left side the week numbers are displayed for each row.
- **Day cell states** Hovering a day highlights the day number with a gray background. Selecting a day highlights the day number with a blue background. The current date is highlighted by default and remains blue when the user selects another date.

## Best for

- **Quarterly planning:** Choosing dates for quarterly milestones, reviews, or deadlines while keeping the full three-month period visible.
- **Annual planning:** Finding and selecting dates for yearly programs, renewal cycles, or recurring business activities with all 12 months available in one view.
- **Seasonal scheduling:** Selecting dates for campaigns, enrollment periods, or other activities organized around a particular quarter or season.
- **Long-range date selection:** Moving between an annual overview and a focused quarter view when the target date may be several months away.

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/react/datepicker/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/react/datepicker/api#opt-themeVariant) - option
- [`display`](https://mobiscroll.com/docs/react/datepicker/api#opt-display) - option
- [`calendarType`](https://mobiscroll.com/docs/react/datepicker/api#opt-calendarType) - option
- [`calendarSize`](https://mobiscroll.com/docs/react/datepicker/api#opt-calendarSize) - option
- [`refDate`](https://mobiscroll.com/docs/react/datepicker/api#opt-refDate) - option
- [`renderCalendarHeader`](https://mobiscroll.com/docs/react/datepicker/api#renderer-renderCalendarHeader) - renderer
- [`onPageChange`](https://mobiscroll.com/docs/react/datepicker/api#event-onPageChange) - event
- [`onChange`](https://mobiscroll.com/docs/react/datepicker/api#event-onChange) - event
- [`showWeekNumbers`](https://mobiscroll.com/docs/react/datepicker/api#opt-showWeekNumbers) - option
- [`controls`](https://mobiscroll.com/docs/react/datepicker/api#opt-controls) - option

## Code

### JSX

```jsx
import {
  CalendarNav,
  CalendarNext,
  CalendarPrev,
  CalendarToday,
  Datepicker,
  Segmented,
  SegmentedGroup,
  setOptions,
} from '@mobiscroll/react';
import { useCallback, useMemo, useState } from 'react';

setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

function App() {
  const [type, setType] = useState('q4');
  const [selectedDate, setDate] = useState(new Date());
  const [activeDate, setActiveDate] = useState(new Date());

  const calType = useMemo(() => (type === 'year' ? 'year' : 'month'), [type]);

  const handleTypeChange = useCallback(
    (event) => {
      const tp = event.target.value;
      const year = activeDate.getFullYear();
      let date;
      switch (tp) {
        case 'q1':
          date = new Date(year, 0, 1);
          break;
        case 'q2':
          date = new Date(year, 3, 1);
          break;
        case 'q3':
          date = new Date(year, 6, 1);
          break;
        case 'q4':
          date = new Date(year, 9, 1);
          break;
        case 'year':
          date = new Date(year, activeDate.getMonth(), 1);
          break;
        default:
          break;
      }
      setType(tp);
      setDate(date);
      setActiveDate(date);
    },
    [activeDate],
  );

  const handlePageChange = useCallback(
    (event) => {
      let t = '';
      if (type === 'year') {
        t = 'year';
      } else {
        const month = event.firstDay.getMonth();
        if (month < 3) {
          t = 'q1';
        } else if (month < 6) {
          t = 'q2';
        } else if (month < 9) {
          t = 'q3';
        } else {
          t = 'q4';
        }
      }
      setTimeout(() => {
        setActiveDate(event.firstDay);
        setType(t);
      });
    },
    [type],
  );

  const handleDateChange = useCallback((event) => {
    setDate(event.value);
  }, []);

  const calendarHeaderSwitch = useCallback(
    () => (
      <>
        <CalendarNav />
        <div className="quarter-year-header-picker">
          <SegmentedGroup value={type} onChange={handleTypeChange}>
            <Segmented value="q1">Q1</Segmented>
            <Segmented value="q2">Q2</Segmented>
            <Segmented value="q3">Q3</Segmented>
            <Segmented value="q4">Q4</Segmented>
            <Segmented value="year">Year</Segmented>
          </SegmentedGroup>
        </div>
        <CalendarPrev />
        <CalendarToday />
        <CalendarNext />
      </>
    ),
    [handleTypeChange, type],
  );

  return (
    <Datepicker
      display="inline"
      calendarType={calType}
      calendarSize={3}
      refDate="1970-01-01"
      renderCalendarHeader={calendarHeaderSwitch}
      onPageChange={handlePageChange}
      onChange={handleDateChange}
      showWeekNumbers={true}
      value={selectedDate}
    />
  );
}

export default App;
```

### TSX

```tsx
import {
  CalendarNav,
  CalendarNext,
  CalendarPrev,
  CalendarToday,
  Datepicker,
  MbscDatepickerChangeEvent,
  MbscDatepickerPageChangeEvent,
  Segmented,
  SegmentedGroup,
  setOptions,
} from '@mobiscroll/react';
import { ChangeEvent, FC, useCallback, useMemo, useState } from 'react';

setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

const App: FC = () => {
  const [type, setType] = useState<string>('q4');
  const calType = useMemo<'year' | 'month' | 'week' | undefined>(() => (type === 'year' ? 'year' : 'month'), [type]);
  const [selectedDate, setDate] = useState<Date>(new Date());
  const [activeDate, setActiveDate] = useState<Date>(new Date());

  const handleTypeChange = useCallback(
    (event: ChangeEvent<HTMLInputElement>) => {
      const tp = event.target.value;
      const year = activeDate.getFullYear();
      let date;
      switch (tp) {
        case 'q1':
          date = new Date(year, 0, 1);
          break;
        case 'q2':
          date = new Date(year, 3, 1);
          break;
        case 'q3':
          date = new Date(year, 6, 1);
          break;
        case 'q4':
          date = new Date(year, 9, 1);
          break;
        case 'year':
          date = new Date(year, activeDate.getMonth(), 1);
          break;
        default:
          break;
      }
      setType(tp);
      setDate(date!);
      setActiveDate(date!);
    },
    [activeDate],
  );

  const handlePageChange = useCallback(
    (event: MbscDatepickerPageChangeEvent) => {
      let t = '';
      if (type === 'year') {
        t = 'year';
      } else {
        const month = event.firstDay.getMonth();
        if (month < 3) {
          t = 'q1';
        } else if (month < 6) {
          t = 'q2';
        } else if (month < 9) {
          t = 'q3';
        } else {
          t = 'q4';
        }
      }
      setTimeout(() => {
        setActiveDate(event.firstDay);
        setType(t);
      });
    },
    [type],
  );

  const handleDateChange = useCallback((event: MbscDatepickerChangeEvent) => {
    setDate(new Date(event.value as string));
  }, []);

  const calendarHeaderSwitch = useCallback(
    () => (
      <>
        <CalendarNav />
        <div className="quarter-year-header-picker">
          <SegmentedGroup value={type} onChange={handleTypeChange}>
            <Segmented value="q1">Q1</Segmented>
            <Segmented value="q2">Q2</Segmented>
            <Segmented value="q3">Q3</Segmented>
            <Segmented value="q4">Q4</Segmented>
            <Segmented value="year">Year</Segmented>
          </SegmentedGroup>
        </div>
        <CalendarPrev />
        <CalendarToday />
        <CalendarNext />
      </>
    ),
    [handleTypeChange, type],
  );

  return (
    <Datepicker
      controls={['calendar']}
      display="inline"
      calendarType={calType}
      calendarSize={3}
      refDate="1970-01-01"
      renderCalendarHeader={calendarHeaderSwitch}
      onPageChange={handlePageChange}
      onChange={handleDateChange}
      showWeekNumbers={true}
      value={selectedDate}
    />
  );
};
export default App;
```

### CSS

```css
.mbsc-datepicker .quarter-year-header-picker .mbsc-segmented {
  max-width: 280px;
  margin: 0 auto;
}

.quarter-year-header-picker {
  flex: 1 0 auto;
}
```

## Implementation notes

- Use `controls: ['calendar']`, `display: 'inline'`, and `calendarSize: 3` so the calendar always renders three months in a grid.
- **Quarter view**: `calendarType: 'month'` combined with `calendarSize: 3` renders the current quarter's three months in a multi-month grid.
- **Year view**: setting `calendarType` to `'year'` renders all 12 months of the year in one scrollable layout; `calendarSize` is left at `3` and has no effect in year mode.
- Set `refDate: '1970-01-01'` so the calendar's month grouping always aligns quarter boundaries to months 0/3/6/9, independent of the current date.
- Build a custom header with `renderCalendarHeader` (Angular: `calendarHeaderTemplate`, Vue: `#header` slot) containing `CalendarNav`/`mbsc-calendar-nav` and `CalendarPrev`/`CalendarNext` (`mbsc-calendar-prev`/`mbsc-calendar-next`); the React version additionally includes a `CalendarToday` button that the other frameworks' header markup omits.
- `calendarType` set to `'month'` renders the current quarter's months; set to `'year'` renders the year view. Navigating to a target month involves setting `calendarType` and calling `navigate(date)` to that month's first day (JS/jQuery: `instance.setOptions({ calendarType })` followed by `instance.navigate(date)`; React/Angular/Vue: update the bound `calendarType` state/binding together with the selected date/model).
- Use the `onPageChange` event (Angular: `(onPageChange)`, Vue: `@page-change`) to read `args.firstDay`/`event.firstDay` and determine which quarter or year is now visible.
- Pass `showWeekNumbers: true` to display the week-number column alongside each month grid.

## Run it in your project

1. If not signed up yet, or don't own a license, you can [start a free trial](https://mobiscroll.com/starttrial).

2. Clone the repository:

   ```
   git clone https://github.com/acidb/mobiscroll-demos-react.git
   ```

3. If not installed yet, install the Mobiscroll CLI from NPM:

   ```
   npm install -g @mobiscroll/cli
   ```

4. Config Mobiscroll - open a terminal window in the root directory and run:

   ```
   mobiscroll config react
   ```

5. Install dependencies - open a terminal window in the root directory and run:

   ```
   npm install
   ```

6. Run the project:

   ```
   npm run dev
   ```

On a trial, add `--trial` to the config command: `mobiscroll config react --trial`.

Running a demo locally requires a Mobiscroll trial or a license; setup is per framework at https://mobiscroll.com/docs/react/getting-started/installation

## Related demos

- Previous: [Multiple Months](https://demo.mobiscroll.com/react/calendar/multiple-months.md) - Show fixed or auto-fit months in the React date picker for travel, booking, and long-range date selection.
- Next: [Week & Month Views](https://demo.mobiscroll.com/react/calendar/week-to-month.md) - Show week and month calendar views in the React date picker — add a header view switcher so users can toggle between layouts.
