MonthView

Standalone schedule month view component

Usage

MonthView displays events in a calendar month grid. It shows event badges in each day cell with support for drag and drop.

Mon
Tue
Wed
Thu
Fri
Sat
Sun
import { MonthView } from '@mantine/schedule';
import { events } from './data';

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

With week numbers

Set withWeekNumbers to display week numbers in the first column.

Mon
Tue
Wed
Thu
Fri
Sat
Sun
import { MonthView } from '@mantine/schedule';
import { events } from './data';

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

Without week days

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

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

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

First day of week

Set firstDayOfWeek to control which day starts the week.

Sun
Mon
Tue
Wed
Thu
Fri
Sat
import { MonthView } from '@mantine/schedule';
import { events } from './data';

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

Weekday format

Use weekdayFormat prop to customize weekday names.

Mon
Tue
Wed
Thu
Fri
Sat
Sun
import { MonthView } from '@mantine/schedule';
import { events } from './data';

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

Consistent weeks

Set consistentWeeks={false} to only show weeks that have days in the current month.

Mon
Tue
Wed
Thu
Fri
Sat
Sun
import dayjs from 'dayjs';
import { useState } from 'react';
import { MonthView } from '@mantine/schedule';

function Demo() {
  const [date, setDate] = useState(dayjs().format('YYYY-MM-DD'));
  return <MonthView date={date} consistentWeeks={false} onDateChange={setDate} />;
}

Highlight today

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

Mon
Tue
Wed
Thu
Fri
Sat
Sun
import { MonthView } from '@mantine/schedule';
import { events } from './data';

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

Without outside days

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

Mon
Tue
Wed
Thu
Fri
Sat
Sun
import { MonthView } from '@mantine/schedule';
import { events } from './data';

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

Without header

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

Mon
Tue
Wed
Thu
Fri
Sat
Sun
import { MonthView } from '@mantine/schedule';
import { events } from './data';

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

Many events

When a day has many events, the component shows a "More events" indicator.

Mon
Tue
Wed
Thu
Fri
Sat
Sun
import { MonthView } from '@mantine/schedule';
import { events } from './data';

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

Drag and drop

Enable drag and drop to move events between days.

Mon
Tue
Wed
Thu
Fri
Sat
Sun
import { useState } from 'react';
import dayjs from 'dayjs';
import { MonthView, ScheduleEventData } from '@mantine/schedule';

const today = dayjs().format('YYYY-MM-DD');

const initialEvents: ScheduleEventData[] = [
  {
    id: 1,
    title: 'Team Meeting',
    start: `${today} 09:00:00`,
    end: `${today} 10:30:00`,
    color: 'blue',
  },
  {
    id: 2,
    title: 'Project Deadline',
    start: dayjs().add(5, 'day').format('YYYY-MM-DD 00:00:00'),
    end: dayjs().add(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss'),
    color: 'red',
  },
];

function Demo() {
  const [events, setEvents] = useState(initialEvents);

  const handleEventDrop = (eventId: string | number, newStart: string, newEnd: string) => {
    setEvents((prev) =>
      prev.map((event) =>
        event.id === eventId ? { ...event, start: newStart, end: newEnd } : event
      )
    );
  };

  return <MonthView date={new Date()} events={events} withEventsDragAndDrop onEventDrop={handleEventDrop} />;
}

Full event customization

Use renderEvent prop to fully customize event rendering. This function receives the event data as the first argument and all props that would be passed to the event root element (including children) as the second argument, allowing you to wrap events in custom components like HoverCard, Tooltip, or custom wrappers.

Mon
Tue
Wed
Thu
Fri
Sat
Sun
import { useState } from 'react';
import dayjs from 'dayjs';
import { HoverCard, UnstyledButton } from '@mantine/core';
import { MonthView, ScheduleEventData } from '@mantine/schedule';
import { EventDetails } from './EventDetails';
import { eventsData } from './events';

function Demo() {
  const [events, setEvents] = useState<ScheduleEventData[]>(eventData);

  return (
    <MonthView
      date={new Date()}
      events={selectedEvents}
      withEventsDragAndDrop
      onEventDrop={(eventId, newStart, newEnd) => {
        setEvents((current) =>
          current.map((event) =>
            event.id === eventId
              ? {
                  ...event,
                  start: dayjs(newStart).format('YYYY-MM-DD HH:mm:ss'),
                  end: dayjs(newEnd).format('YYYY-MM-DD HH:mm:ss'),
                }
              : event
          )
        );
      }}
      renderEvent={(event, props) => (
        <HoverCard width={280} position="right" closeDelay={0} transitionProps={{ duration: 0 }}>
          <HoverCard.Target>
            <UnstyledButton {...props} />
          </HoverCard.Target>
          <HoverCard.Dropdown>
            <EventDetails event={event} />
          </HoverCard.Dropdown>
        </HoverCard>
      )}
    />
  );
}

Static mode

Set mode="static" to disable all interactions.

Mon
Tue
Wed
Thu
Fri
Sat
Sun
import { MonthView } from '@mantine/schedule';
import { events } from './data';

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

Create and update events

Set withDragSlotSelect prop to allow users to drag across day cells to select a date range. When the drag ends, the onSlotDragEnd callback is called with the range start and end dates. Combined with onDayClick and onEventClick callbacks, this enables a complete event creation and editing experience.

Mon
Tue
Wed
Thu
Fri
Sat
Sun
import dayjs from 'dayjs';
import { useState } from 'react';
import { MonthView, ScheduleEventData } from '@mantine/schedule';
import { EventData, EventForm } from './_EventForm';
import { events } from './events';

function Demo() {
  const [events, setEvents] = useState<ScheduleEventData[]>(events);
  const [formOpened, setFormOpened] = useState(false);
  const [selectedEventData, setSelectedEventData] = useState<EventData | null>(null);

  const handleDayClick = (date: string) => {
    setSelectedEventData({
      title: '',
      start: dayjs(date).startOf('day').toDate(),
      end: dayjs(date).endOf('day').toDate(),
      color: 'blue',
    });
    setFormOpened(true);
  };

  const handleEventClick = (event: ScheduleEventData) => {
    setSelectedEventData({
      id: event.id,
      title: event.title,
      start: new Date(event.start),
      end: new Date(event.end),
      color: event.color || 'blue',
    });
    setFormOpened(true);
  };

  const handleSubmit = (values: EventData) => {
    if (values.id) {
      setEvents((prev) =>
        prev.map((event) =>
          event.id === values.id
            ? {
                ...event,
                title: values.title,
                start: dayjs(values.start).toISOString(),
                end: dayjs(values.end).toISOString(),
                color: values.color || 'blue',
              }
            : event
        )
      );
    } else {
      setEvents((prev) => [
        ...prev,
        {
          id: Math.random().toString(36).substring(2, 11),
          title: values.title,
          start: dayjs(values.start).toISOString(),
          end: dayjs(values.end).toISOString(),
          color: values.color || 'blue',
        },
      ]);
    }
  };

  const handleSlotDragEnd = (rangeStart: string, rangeEnd: string) => {
    setSelectedEventData({
      title: '',
      start: new Date(rangeStart),
      end: new Date(rangeEnd),
      color: 'blue',
    });
    setFormOpened(true);
  };

  const handleDeleteEvent = () => {
    if (selectedEventData?.id) {
      setEvents((prev) => prev.filter((e) => e.id !== selectedEventData.id));
    }
  };

  return (
    <>
      <MonthView
        date={new Date()}
        events={events}
        withDragSlotSelect
        onDayClick={handleDayClick}
        onSlotDragEnd={handleSlotDragEnd}
        onEventClick={handleEventClick}
      />

      <EventForm
        opened={formOpened}
        onClose={() => setFormOpened(false)}
        onExitTransitionEnd={() => setSelectedEventData(null)}
        values={selectedEventData}
        onSubmit={handleSubmit}
        onDelete={selectedEventData?.id ? handleDeleteEvent : undefined}
      />
    </>
  );
}

Accessibility

Focus management

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

  • Only the first day in the month view is included in the tab order (has tabIndex={0})
  • All other days have tabIndex={-1} and can only be reached via arrow key navigation
  • Outside days (days from adjacent months) are navigable using arrow keys when withOutsideDays is true
  • 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 through the view 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
ArrowUpFocuses same day in the previous week

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.