ResourcesMonthView

Month view with resource lanes for scheduling across resources

Usage

ResourcesMonthView displays a month grid where each day cell contains resource lanes. Events are shown within their resource's lane, making it easy to see resource utilization across the month.

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31
Meeting room: Tokyo
Meeting room: Paris
Meeting room: New York
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesMonthView } from '@mantine/schedule';
import { events, resources } from './data';

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

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      startScrollDate={dayjs().format('YYYY-MM-DD')}
    />
  );
}

Static mode

Set mode="static" to disable all interactions.

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31
Meeting room: Tokyo
Meeting room: Paris
Meeting room: New York
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesMonthView } from '@mantine/schedule';
import { events, resources } from './data';

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

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      mode="static"
      startScrollDate={dayjs().format('YYYY-MM-DD')}
    />
  );
}

Drag and drop

Enable drag and drop with withEventsDragAndDrop prop. The onEventDrop callback receives the resourceId of the target resource, allowing you to update the event's resource assignment.

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31
Meeting room: Tokyo
Meeting room: Paris
Meeting room: New York
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesMonthView, ScheduleEventData } from '@mantine/schedule';
import { events as initialEvents, resources } from './data';

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

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      withEventsDragAndDrop
      onEventDrop={({ eventId, newStart, newEnd, resourceId }) => {
        setEvents((current) =>
          current.map((event) =>
            event.id === eventId
              ? { ...event, start: newStart, end: newEnd, resourceId }
              : event
          )
        );
      }}
      startScrollDate={dayjs().format('YYYY-MM-DD')}
    />
  );
}

Event form

Click a day cell to create a new event, or click an existing event to edit it. Use withDragSlotSelect and onSlotDragEnd to allow drag-to-create across day cells. The onSlotDragEnd callback includes the resourceId of the resource where the drag started.

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31
Meeting room: Tokyo
Meeting room: Paris
Meeting room: New York
import dayjs from 'dayjs';
import { useState } from 'react';
import { Select } from '@mantine/core';
import { ResourcesMonthView, ScheduleEventData } from '@mantine/schedule';
import { EventData, EventForm } from './EventForm';
import { events as initialEvents, resources } from './data';

function Demo() {
  const [date, setDate] = useState(dayjs().format('YYYY-MM-DD'));
  const [events, setEvents] = useState<ScheduleEventData[]>(initialEvents);
  const [formOpened, setFormOpened] = useState(false);
  const [selectedEventData, setSelectedEventData] = useState<EventData | null>(null);
  const [selectedResourceId, setSelectedResourceId] = useState<string | null>(null);

  const handleDayClick = ({
    date: clickedDate,
    resourceId,
  }: {
    date: string;
    nativeEvent: React.MouseEvent<HTMLButtonElement>;
    resourceId?: string | number;
  }) => {
    setSelectedResourceId(resourceId ? String(resourceId) : null);
    setSelectedEventData({
      title: '',
      start: dayjs(clickedDate).startOf('day').toDate(),
      end: dayjs(clickedDate).endOf('day').toDate(),
      color: 'blue',
    });
    setFormOpened(true);
  };

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

  const handleSlotDragEnd = ({
    rangeStart,
    rangeEnd,
    resourceId,
  }: {
    rangeStart: string;
    rangeEnd: string;
    resourceId?: string | number;
  }) => {
    setSelectedResourceId(resourceId ? String(resourceId) : null);
    setSelectedEventData({
      title: '',
      start: new Date(rangeStart),
      end: new Date(rangeEnd),
      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',
                resourceId: selectedResourceId || event.resourceId,
              }
            : 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',
          resourceId: selectedResourceId || undefined,
        },
      ]);
    }
  };

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

  return (
    <>
      <ResourcesMonthView
        date={date}
        onDateChange={setDate}
        resources={resources}
        events={events}
        withDragSlotSelect
        onDayClick={handleDayClick}
        onSlotDragEnd={handleSlotDragEnd}
        onEventClick={handleEventClick}
        startScrollDate={dayjs().format('YYYY-MM-DD')}
      />

      <EventForm
        opened={formOpened}
        onClose={() => setFormOpened(false)}
        onExitTransitionEnd={() => setSelectedEventData(null)}
        values={selectedEventData}
        onSubmit={handleSubmit}
        onDelete={selectedEventData?.id ? handleDeleteEvent : undefined}
      >
        <Select
          label="Resource"
          placeholder="Select resource"
          radius="md"
          data={resources.map((r) => ({ value: String(r.id), label: r.label }))}
          value={selectedResourceId}
          onChange={setSelectedResourceId}
        />
      </EventForm>
    </>
  );
}

Custom event rendering

Use renderEvent prop to customize how events are rendered. The example below uses HoverCard to display event details on hover.

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31
Meeting room: Tokyo
Meeting room: Paris
Meeting room: New York
import dayjs from 'dayjs';
import { useState } from 'react';
import { HoverCard, UnstyledButton } from '@mantine/core';
import { ResourcesMonthView, ScheduleEventData } from '@mantine/schedule';
import { EventDetails } from './EventDetails';
import { events as initialEvents, resources } from './data';

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

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      withEventsDragAndDrop
      onEventDrop={({ eventId, newStart, newEnd, resourceId }) => {
        setEvents((current) =>
          current.map((event) =>
            event.id === eventId
              ? { ...event, start: newStart, end: newEnd, resourceId }
              : 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} resources={resources} />
          </HoverCard.Dropdown>
        </HoverCard>
      )}
      startScrollDate={dayjs().format('YYYY-MM-DD')}
    />
  );
}

Custom resource label

Use renderResourceLabel prop to customize how resource labels are rendered in the row headers.

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31

Meeting room: Tokyo

Office

Meeting room: Paris

Office

Meeting room: New York

Office

import dayjs from 'dayjs';
import { useState } from 'react';
import { Stack, Text } from '@mantine/core';
import { ResourcesMonthView, ScheduleResourceData } from '@mantine/schedule';
import { events, resources } from './data';

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

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      renderResourceLabel={(resource: ScheduleResourceData) => (
        <Stack gap={0} align="flex-start">
          <Text fw={600} size="sm">
            {resource.label}
          </Text>
          <Text size="xs" c="dimmed">
            Office
          </Text>
        </Stack>
      )}
      startScrollDate={dayjs().format('YYYY-MM-DD')}
    />
  );
}

Resource groups

Use groups prop to group resources under labeled headers. The group labels are displayed as a column to the left of resource labels, spanning vertically across their resources. Use renderGroupLabel to customize group label rendering and groupLabelWidth to control the group column width.

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31
Floor 1
Meeting room: Tokyo
Meeting room: Paris
Floor 2
Meeting room: New York
Overflow room
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesMonthView, ScheduleResourceGroup } from '@mantine/schedule';
import { events } from './data';

const resources = [
  { id: 'tokyo', label: 'Meeting room: Tokyo' },
  { id: 'paris', label: 'Meeting room: Paris' },
  { id: 'new-york', label: 'Meeting room: New York' },
  { id: 'overflow', label: 'Overflow room' },
];

const groups: ScheduleResourceGroup[] = [
  { label: 'Floor 1', resourceIds: ['tokyo', 'paris'] },
  { label: 'Floor 2', resourceIds: ['new-york'] },
];

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

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      groups={groups}
      startScrollDate={dayjs().format('YYYY-MM-DD')}
    />
  );
}

Localization

Set locale prop to change the language. Use labels prop to override built-in labels.

Recursos
mié.1
jue.2
vie.3
sáb.4
dom.5
lun.6
mar.7
mié.8
jue.9
vie.10
sáb.11
dom.12
lun.13
mar.14
mié.15
jue.16
vie.17
sáb.18
dom.19
lun.20
mar.21
mié.22
jue.23
vie.24
sáb.25
dom.26
lun.27
mar.28
mié.29
jue.30
vie.31
Meeting room: Tokyo
Meeting room: Paris
Meeting room: New York
import 'dayjs/locale/es';

import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesMonthView } from '@mantine/schedule';
import { events, resources } from './data';

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

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      locale="es"
      labels={{
        day: 'Día',
        week: 'Semana',
        month: 'Mes',
        year: 'Año',
        today: 'Hoy',
        previous: 'Anterior',
        next: 'Siguiente',
        more: 'Más',
        resources: 'Recursos',
        moreLabel: (hiddenEventsCount) => `+${hiddenEventsCount} más`,
      }}
      startScrollDate={dayjs().format('YYYY-MM-DD')}
    />
  );
}

Recurring events

ResourcesMonthView automatically expands recurring events for the visible month. See Recurring events guide for full documentation.

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31
Meeting room: Tokyo
Meeting room: Paris
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesMonthView } from '@mantine/schedule';

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

const resources = [
  { id: 'tokyo', label: 'Meeting room: Tokyo' },
  { id: 'paris', label: 'Meeting room: Paris' },
];

const events = [
  {
    id: 'daily-sync-series',
    title: 'Daily sync (series)',
    start: `${dayjs(today).subtract(2, 'day').format('YYYY-MM-DD')} 09:00:00`,
    end: `${dayjs(today).subtract(2, 'day').format('YYYY-MM-DD')} 09:30:00`,
    color: 'blue',
    resourceId: 'tokyo',
    recurrence: {
      rrule: 'FREQ=DAILY;COUNT=10',
      exdate: [`${today} 09:00:00`],
    },
  },
  {
    id: 'daily-sync-override',
    title: 'Daily sync (moved today)',
    start: `${today} 14:00:00`,
    end: `${today} 14:30:00`,
    color: 'grape',
    resourceId: 'tokyo',
    recurringEventId: 'daily-sync-series',
    recurrenceId: `${today} 09:00:00`,
  },
  {
    id: 'one-off',
    title: 'One-off planning',
    start: `${today} 11:00:00`,
    end: `${today} 12:00:00`,
    color: 'green',
    resourceId: 'paris',
  },
];

function Demo() {
  const [date, setDate] = useState(today);

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      startScrollDate={today}
    />
  );
}

Max events per cell

Use maxEventsPerTimeSlot prop to limit the number of events visible in each cell. When there are more events than the limit, a "+more" indicator is shown.

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31
Meeting room: Tokyo
Meeting room: Paris
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesMonthView, ScheduleEventData, ScheduleResourceData } from '@mantine/schedule';

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

const resources: ScheduleResourceData[] = [
  { id: 'tokyo', label: 'Meeting room: Tokyo' },
  { id: 'paris', label: 'Meeting room: Paris' },
];

const events: ScheduleEventData[] = [
  { id: 1, title: 'Team Meeting', start: `${today} 09:00:00`, end: `${today} 10:00:00`, color: 'blue', resourceId: 'tokyo' },
  { id: 2, title: 'Code Review', start: `${today} 11:00:00`, end: `${today} 12:00:00`, color: 'green', resourceId: 'tokyo' },
  { id: 3, title: 'Design Review', start: `${today} 13:00:00`, end: `${today} 14:00:00`, color: 'violet', resourceId: 'tokyo' },
  { id: 4, title: 'Sprint Planning', start: `${today} 15:00:00`, end: `${today} 16:00:00`, color: 'orange', resourceId: 'tokyo' },
  { id: 5, title: 'Retrospective', start: `${today} 16:30:00`, end: `${today} 17:30:00`, color: 'cyan', resourceId: 'tokyo' },
  { id: 6, title: 'Client Call', start: `${today} 09:00:00`, end: `${today} 10:00:00`, color: 'pink', resourceId: 'paris' },
  { id: 7, title: 'Workshop', start: `${today} 11:00:00`, end: `${today} 12:00:00`, color: 'grape', resourceId: 'paris' },
  { id: 8, title: 'Standup', start: `${today} 14:00:00`, end: `${today} 15:00:00`, color: 'red', resourceId: 'paris' },
  { id: 9, title: 'Demo', start: `${today} 16:00:00`, end: `${today} 17:00:00`, color: 'teal', resourceId: 'paris' },
];

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

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      maxEventsPerTimeSlot={3}
      startScrollDate={dayjs().format('YYYY-MM-DD')}
    />
  );
}

Day width and row height

Use dayWidth and rowHeight props to customize the dimensions of day columns and resource rows.

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31
Meeting room: Tokyo
Meeting room: Paris
Meeting room: New York
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesMonthView } from '@mantine/schedule';
import { events, resources } from './data';

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

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      dayWidth={60}
      rowHeight={64}
      startScrollDate={dayjs().format('YYYY-MM-DD')}
    />
  );
}

Start scroll date

Use startScrollDate prop to scroll to a specific date on initial render. This is useful when the month has many days and you want the view to start at a specific date, for example today.

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31
Meeting room: Tokyo
Meeting room: Paris
Meeting room: New York
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesMonthView } from '@mantine/schedule';
import { events, resources } from './data';

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

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      startScrollDate={dayjs().format('YYYY-MM-DD')}
    />
  );
}

Without weekend days

Set withWeekendDays={false} to hide weekend day columns from the month grid.

Resources
Wed1
Thu2
Fri3
Mon6
Tue7
Wed8
Thu9
Fri10
Mon13
Tue14
Wed15
Thu16
Fri17
Mon20
Tue21
Wed22
Thu23
Fri24
Mon27
Tue28
Wed29
Thu30
Fri31
Meeting room: Tokyo
Meeting room: Paris
Meeting room: New York
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesMonthView } from '@mantine/schedule';
import { events, resources } from './data';

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

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      withWeekendDays={false}
      startScrollDate={dayjs().format('YYYY-MM-DD')}
    />
  );
}

More events indicator

When a day cell has more events than maxEventsPerTimeSlot, a "+X more" indicator is shown. Click the indicator to see all events for that day.

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31
Meeting room: Tokyo
Meeting room: Paris
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesMonthView, ScheduleEventData, ScheduleResourceData } from '@mantine/schedule';

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

const resources: ScheduleResourceData[] = [
  { id: 'tokyo', label: 'Meeting room: Tokyo' },
  { id: 'paris', label: 'Meeting room: Paris' },
];

const events: ScheduleEventData[] = [
  { id: 1, title: 'Team Standup', start: `${today} 08:00:00`, end: `${today} 08:30:00`, color: 'blue', resourceId: 'tokyo' },
  { id: 2, title: 'Sprint Planning', start: `${today} 09:00:00`, end: `${today} 10:00:00`, color: 'green', resourceId: 'tokyo' },
  { id: 3, title: 'Design Review', start: `${today} 10:30:00`, end: `${today} 11:30:00`, color: 'violet', resourceId: 'tokyo' },
  { id: 4, title: 'Code Review', start: `${today} 13:00:00`, end: `${today} 14:00:00`, color: 'orange', resourceId: 'tokyo' },
  { id: 5, title: 'Retrospective', start: `${today} 14:30:00`, end: `${today} 15:30:00`, color: 'cyan', resourceId: 'tokyo' },
  { id: 6, title: 'Client Call', start: `${today} 16:00:00`, end: `${today} 17:00:00`, color: 'pink', resourceId: 'tokyo' },
  { id: 7, title: 'Workshop', start: `${today} 10:00:00`, end: `${today} 11:00:00`, color: 'grape', resourceId: 'paris' },
];

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

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      maxEventsPerTimeSlot={2}
      startScrollDate={dayjs().format('YYYY-MM-DD')}
    />
  );
}

More events props

Use moreEventsProps to customize the more events dropdown, for example to use a modal instead of a popover.

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31
Meeting room: Tokyo
Meeting room: Paris
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesMonthView, ScheduleEventData, ScheduleResourceData } from '@mantine/schedule';

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

const resources: ScheduleResourceData[] = [
  { id: 'tokyo', label: 'Meeting room: Tokyo' },
  { id: 'paris', label: 'Meeting room: Paris' },
];

const events: ScheduleEventData[] = [
  { id: 1, title: 'Team Standup', start: `${today} 08:00:00`, end: `${today} 08:30:00`, color: 'blue', resourceId: 'tokyo' },
  { id: 2, title: 'Sprint Planning', start: `${today} 09:00:00`, end: `${today} 10:00:00`, color: 'green', resourceId: 'tokyo' },
  { id: 3, title: 'Design Review', start: `${today} 10:30:00`, end: `${today} 11:30:00`, color: 'violet', resourceId: 'tokyo' },
  { id: 4, title: 'Code Review', start: `${today} 13:00:00`, end: `${today} 14:00:00`, color: 'orange', resourceId: 'tokyo' },
  { id: 5, title: 'Retrospective', start: `${today} 14:30:00`, end: `${today} 15:30:00`, color: 'cyan', resourceId: 'tokyo' },
  { id: 6, title: 'Client Call', start: `${today} 16:00:00`, end: `${today} 17:00:00`, color: 'pink', resourceId: 'tokyo' },
  { id: 7, title: 'Workshop', start: `${today} 10:00:00`, end: `${today} 11:00:00`, color: 'grape', resourceId: 'paris' },
];

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

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      maxEventsPerTimeSlot={2}
      startScrollDate={dayjs().format('YYYY-MM-DD')}
      moreEventsProps={{ dropdownType: 'modal', modalTitle: 'All Events' }}
    />
  );
}

Custom header

Set withHeader={false} to hide the default header and use ScheduleHeader to build a custom header with navigation controls.

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31
Meeting room: Tokyo
Meeting room: Paris
Meeting room: New York
import dayjs from 'dayjs';
import { useState } from 'react';
import { DateStringValue, ResourcesMonthView, ScheduleHeader } from '@mantine/schedule';
import { events, resources } 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, 'month')
                .startOf('month')
                .format('YYYY-MM-DD') as DateStringValue
            )
          }
        />
        <ScheduleHeader.MonthYearSelect
          yearValue={dayjs(date).year()}
          monthValue={dayjs(date).month()}
          onYearChange={(year) =>
            setDate(
              dayjs(date)
                .year(year)
                .startOf('month')
                .format('YYYY-MM-DD') as DateStringValue
            )
          }
          onMonthChange={(month) =>
            setDate(
              dayjs(date)
                .month(month)
                .startOf('month')
                .format('YYYY-MM-DD') as DateStringValue
            )
          }
        />
        <ScheduleHeader.Next
          onClick={() =>
            setDate(
              dayjs(date)
                .add(1, 'month')
                .startOf('month')
                .format('YYYY-MM-DD') as DateStringValue
            )
          }
        />
        <ScheduleHeader.Today
          onClick={() =>
            setDate(
              dayjs().format('YYYY-MM-DD') as DateStringValue
            )
          }
        />
      </ScheduleHeader>

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

Radius

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31
Meeting room: Tokyo
Meeting room: Paris
Meeting room: New York
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesMonthView } from '@mantine/schedule';
import { events, resources } from './data';

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

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      radius="md"
      startScrollDate={dayjs().format('YYYY-MM-DD')}
    />
  );
}

Scroll area props

Use scrollAreaProps to customize the scroll area behavior.

Resources
Wed1
Thu2
Fri3
Sat4
Sun5
Mon6
Tue7
Wed8
Thu9
Fri10
Sat11
Sun12
Mon13
Tue14
Wed15
Thu16
Fri17
Sat18
Sun19
Mon20
Tue21
Wed22
Thu23
Fri24
Sat25
Sun26
Mon27
Tue28
Wed29
Thu30
Fri31
Meeting room: Tokyo
Meeting room: Paris
Meeting room: New York
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesMonthView } from '@mantine/schedule';
import { events, resources } from './data';

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

  return (
    <ResourcesMonthView
      date={date}
      onDateChange={setDate}
      resources={resources}
      events={events}
      scrollAreaProps={{
        scrollbarSize: 10,
        offsetScrollbars: true,
        type: 'always',
        scrollbars: 'x',
      }}
      startScrollDate={dayjs().format('YYYY-MM-DD')}
    />
  );
}