YearView

Standalone schedule year view component

Usage

YearView displays all 12 months of a year in a grid layout. Event indicators are shown as colored dots in day cells.

M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
import dayjs from 'dayjs';
import { useState } from 'react';
import { YearView } from '@mantine/schedule';
import { events } from './data';

function Demo() {
  const [date, setDate] = useState(dayjs().format('YYYY-MM-DD'));

  return (
    <YearView
      date={date}
      onDateChange={setDate}
      events={events}
    />
  );
}

With week numbers

Set withWeekNumbers to display week numbers in the first column of each month.

M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
import { YearView } from '@mantine/schedule';
import { events } from './data';

function Demo() {
  return <YearView date={new Date()} events={events} withWeekNumbers />;
}

Without week days

Set withWeekDays={false} to hide the weekday names row in each month.

import { YearView } from '@mantine/schedule';
import { events } from './data';

function Demo() {
  return <YearView date={new Date()} events={events} withWeekDays={false} />;
}

Render day

Use renderDay to replace the entire content of a day cell. The function is called with the day date in YYYY-MM-DD format and the events grouped on that day – the same list that is used to render the default indicators, but without the three items limit. This makes it possible to display counts, badges or icons instead of the default dots.

Note that when renderDay is set, the default event indicators are not rendered – the cell displays exactly what the function returns. The day cell has position: relative, so absolutely positioned content is placed relative to it.

M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
import dayjs from 'dayjs';
import { YearView } from '@mantine/schedule';
import { events } from './data';

function Demo() {
  return (
    <YearView
      date={new Date()}
      events={events}
      renderDay={(date, dayEvents) => (
        <>
          {dayjs(date).date()}

          {dayEvents.length > 0 && (
            <div
              style={{
                position: 'absolute',
                bottom: 0,
                insetInlineEnd: 0,
                minWidth: 12,
                height: 12,
                borderRadius: 12,
                fontSize: 9,
                lineHeight: '12px',
                fontWeight: 700,
                textAlign: 'center',
                color: 'var(--mantine-color-white)',
                backgroundColor: `var(--mantine-color-${dayEvents[0].color}-filled)`,
              }}
            >
              {dayEvents.length}
            </div>
          )}
        </>
      )}
    />
  );
}

Hide weekend days

Set withWeekendDays={false} to hide weekend days. Every month grid shrinks to the remaining columns and events that fall only on hidden days are not displayed. The days that are considered weekend are controlled by the weekendDays prop (or DatesProvider, [0, 6] by default).

M
T
W
T
F
M
T
W
T
F
M
T
W
T
F
M
T
W
T
F
M
T
W
T
F
M
T
W
T
F
M
T
W
T
F
M
T
W
T
F
M
T
W
T
F
M
T
W
T
F
M
T
W
T
F
M
T
W
T
F
import { YearView } from '@mantine/schedule';
import { events } from './data';

function Demo() {
  return <YearView date={new Date()} events={events} withWeekendDays={false} />;
}

Hide outside days

Set withOutsideDays={false} to hide days from adjacent months.

M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
import { YearView } from '@mantine/schedule';
import { events } from './data';

function Demo() {
  return <YearView date={new Date()} events={events} withOutsideDays={false} />;
}

First day of week

Set firstDayOfWeek to control which day starts the week.

S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
import { YearView } from '@mantine/schedule';
import { events } from './data';

function Demo() {
  return <YearView date={new Date()} events={events} firstDayOfWeek={0} />;
}

Weekday format

Use weekdayFormat prop to customize weekday names.

Mo
Tu
We
Th
Fr
Sa
Su
Mo
Tu
We
Th
Fr
Sa
Su
Mo
Tu
We
Th
Fr
Sa
Su
Mo
Tu
We
Th
Fr
Sa
Su
Mo
Tu
We
Th
Fr
Sa
Su
Mo
Tu
We
Th
Fr
Sa
Su
Mo
Tu
We
Th
Fr
Sa
Su
Mo
Tu
We
Th
Fr
Sa
Su
Mo
Tu
We
Th
Fr
Sa
Su
Mo
Tu
We
Th
Fr
Sa
Su
Mo
Tu
We
Th
Fr
Sa
Su
Mo
Tu
We
Th
Fr
Sa
Su
import { YearView } from '@mantine/schedule';
import { events } from './data';

function Demo() {
  return <YearView date={new Date()} events={events} weekdayFormat="dd" />;
}

Highlight today

Set highlightToday={false} to disable highlighting the current day.

M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
import { YearView } from '@mantine/schedule';
import { events } from './data';

function Demo() {
  return <YearView date={new Date()} events={events} highlightToday={false} />;
}

Without header

Set withHeader={false} to hide the header controls.

M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
import { YearView } from '@mantine/schedule';
import { events } from './data';

function Demo() {
  return <YearView date={new Date()} events={events} withHeader={false} />;
}

Custom header

You can build a custom header using ScheduleHeader compound components combined with your own controls. Set withHeader={false} on the view and compose the header externally.

M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
import dayjs from 'dayjs';
import { useState } from 'react';
import { DateStringValue, ScheduleHeader, YearView } from '@mantine/schedule';
import { events } from './data';

function Demo() {
  const [date, setDate] = useState<DateStringValue>(
    dayjs().format('YYYY-MM-DD')
  );

  return (
    <div>
      <ScheduleHeader>
        <ScheduleHeader.Previous
          onClick={() =>
            setDate(
              dayjs(date)
                .subtract(1, 'year')
                .startOf('year')
                .format('YYYY-MM-DD') as DateStringValue
            )
          }
        />
        <ScheduleHeader.MonthYearSelect
          withMonths={false}
          yearValue={dayjs(date).year()}
          monthValue={dayjs(date).month()}
          onYearChange={(year) =>
            setDate(
              dayjs(date)
                .year(year)
                .startOf('year')
                .format('YYYY-MM-DD') as DateStringValue
            )
          }
        />
        <ScheduleHeader.Next
          onClick={() =>
            setDate(
              dayjs(date)
                .add(1, 'year')
                .startOf('year')
                .format('YYYY-MM-DD') as DateStringValue
            )
          }
        />
        <ScheduleHeader.Today
          onClick={() =>
            setDate(
              dayjs().format('YYYY-MM-DD') as DateStringValue
            )
          }
        />
        <ScheduleHeader.Control
          style={{ marginInlineStart: 'auto' }}
          onClick={() => window.print()}
        >
          Print
        </ScheduleHeader.Control>
      </ScheduleHeader>

      <YearView
        date={date}
        onDateChange={setDate}
        events={events}
        withHeader={false}
      />
    </div>
  );
}

Recurring events

YearView automatically expands recurring events for the visible year. See Recurring events guide for full documentation.

M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
import dayjs from 'dayjs';
import { useState } from 'react';
import { YearView } from '@mantine/schedule';

const year = dayjs().year();

const events = [
  {
    id: 'yearly-series',
    title: 'Monthly architecture review (series)',
    start: `${year}-01-10 09:00:00`,
    end: `${year}-01-10 10:00:00`,
    color: 'blue',
    recurrence: {
      rrule: 'FREQ=MONTHLY;COUNT=12',
      exdate: [`${year}-11-10 09:00:00`],
    },
  },
  {
    id: 'yearly-series-override',
    title: 'Monthly architecture review (moved in Nov)',
    start: `${year}-11-10 13:00:00`,
    end: `${year}-11-10 14:00:00`,
    color: 'grape',
    recurringEventId: 'yearly-series',
    recurrenceId: `${year}-11-10 09:00:00`,
  },
];

function Demo() {
  const [date, setDate] = useState(`${year}-01-01`);
  return <YearView date={date} onDateChange={setDate} events={events} withWeekNumbers />;
}

Static mode

Set mode="static" to disable all interactions.

M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
M
T
W
T
F
S
S
import { YearView } from '@mantine/schedule';
import { events } from './data';

function Demo() {
  return <YearView date={new Date()} events={events} mode="static" />;
}

Localization

Use locale prop to set the dayjs locale for date formatting. Combine it with labels prop to translate all UI text.

l
m
m
j
v
s
d
l
m
m
j
v
s
d
l
m
m
j
v
s
d
l
m
m
j
v
s
d
l
m
m
j
v
s
d
l
m
m
j
v
s
d
l
m
m
j
v
s
d
l
m
m
j
v
s
d
l
m
m
j
v
s
d
l
m
m
j
v
s
d
l
m
m
j
v
s
d
l
m
m
j
v
s
d
import 'dayjs/locale/es';
import dayjs from 'dayjs';
import { useState } from 'react';
import { YearView } from '@mantine/schedule';
import { events } from './data';

function Demo() {
  const [date, setDate] = useState(dayjs().format('YYYY-MM-DD'));

  return (
    <YearView
      date={date}
      onDateChange={setDate}
      events={events}
      locale="es"
      labels={{
        day: 'Día',
        week: 'Semana',
        month: 'Mes',
        year: 'Año',
        today: 'Hoy',
        previous: 'Anterior',
        next: 'Siguiente',
      }}
    />
  );
}

Responsive styles

YearView uses @container queries for responsive styles. The component automatically adjusts its layout based on the container width, hiding labels and reducing padding on smaller screens. Container queries are supported in all modern browsers.

Accessibility

Focus management

In the YearView component, focus is managed to provide an efficient keyboard navigation experience:

  • Only the first day of each month is included in the tab order (has tabIndex={0})
  • All other days within a month have tabIndex={-1} and can only be reached via arrow key navigation
  • Outside days (days from adjacent months displayed in the current month view) are not focusable
  • Disabled days are skipped during keyboard navigation

This approach reduces the number of tab stops when navigating through the calendar, making it faster for keyboard users to move between months while still allowing full access to all days via arrow keys.

Keyboard interactions

Note that the following events will only trigger if focus is on a day control.

KeyDescription
ArrowRightFocuses next non-disabled day
ArrowLeftFocuses previous non-disabled day
ArrowDownFocuses same day in the next week (moves to next month if at end of month)
ArrowUpFocuses same day in the previous week (moves to previous month if at start)

Day labels

Each day button has an aria-label attribute with the full date in the format "Month Day, Year" (e.g., "November 15, 2025"). This provides screen reader users with complete date information.