ResourcesSchedule
Schedule wrapper component that combines resource day, week and month views
Source
LLM docs
Docs
Package
Usage
ResourcesSchedule is a wrapper component that combines ResourcesDayView, ResourcesWeekView, and ResourcesMonthView
into a single component with view switching. It manages the current view and date state, passing
common props to all three views automatically.
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesSchedule } from '@mantine/schedule';
import { events, resources } from './data';
function Demo() {
const today = dayjs().format('YYYY-MM-DD');
const [date, setDate] = useState(today);
return (
<ResourcesSchedule
date={date}
onDateChange={setDate}
resources={resources}
events={events}
dayViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollTime: '08:00:00' }}
weekViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollDateTime: `${today} 08:00:00` }}
monthViewProps={{ startScrollDate: today }}
/>
);
}Controlled state
You can control the current date and view level with date/onDateChange and view/onViewChange props.
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesSchedule, ResourcesScheduleViewLevel } from '@mantine/schedule';
import { events, resources } from './data';
function Demo() {
const today = dayjs().format('YYYY-MM-DD');
const [date, setDate] = useState(today);
const [view, setView] = useState<ResourcesScheduleViewLevel>('day');
return (
<ResourcesSchedule
date={date}
onDateChange={setDate}
view={view}
onViewChange={setView}
resources={resources}
events={events}
dayViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollTime: '08:00:00' }}
weekViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollDateTime: `${today} 08:00:00` }}
monthViewProps={{ startScrollDate: today }}
/>
);
}Drag and drop
Enable drag and drop across all views with withEventsDragAndDrop prop.
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesSchedule, ScheduleEventData } from '@mantine/schedule';
import { events as initialEvents, resources } from './data';
function Demo() {
const today = dayjs().format('YYYY-MM-DD');
const [date, setDate] = useState(today);
const [events, setEvents] = useState<ScheduleEventData[]>(initialEvents);
return (
<ResourcesSchedule
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
)
);
}}
dayViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollTime: '08:00:00' }}
weekViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollDateTime: `${today} 08:00:00` }}
monthViewProps={{ startScrollDate: today }}
/>
);
}Week view
Set defaultView="week" to open the schedule in week view by default.
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesSchedule } from '@mantine/schedule';
import { events, resources } from './data';
function Demo() {
const today = dayjs().format('YYYY-MM-DD');
const [date, setDate] = useState(today);
return (
<ResourcesSchedule
date={date}
onDateChange={setDate}
resources={resources}
events={events}
defaultView="week"
dayViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollTime: '08:00:00' }}
weekViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollDateTime: `${today} 08:00:00` }}
monthViewProps={{ startScrollDate: today }}
/>
);
}View-specific props
Use dayViewProps, weekViewProps, and monthViewProps to pass props specific to each view.
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesSchedule } from '@mantine/schedule';
import { events, resources } from './data';
function Demo() {
const today = dayjs().format('YYYY-MM-DD');
const [date, setDate] = useState(today);
return (
<ResourcesSchedule
date={date}
onDateChange={setDate}
resources={resources}
events={events}
dayViewProps={{
startTime: '08:00:00',
endTime: '18:00:00',
intervalMinutes: 30,
startScrollTime: '08:00:00',
}}
weekViewProps={{
startTime: '08:00:00',
endTime: '18:00:00',
startScrollDateTime: `${today} 08:00:00`,
}}
monthViewProps={{
maxEventsPerTimeSlot: 3,
startScrollDate: today,
}}
/>
);
}Event form
Click time slots, day cells, or existing events to open an event form. The form works across day, week, and month views with automatic view switching.
import dayjs from 'dayjs';
import { useState } from 'react';
import { Select } from '@mantine/core';
import { ResourcesSchedule, ScheduleEventData } from '@mantine/schedule';
import { EventData, EventForm } from './EventForm';
import { events as initialEvents, resources } from './data';
function Demo() {
const today = dayjs().format('YYYY-MM-DD');
const [date, setDate] = useState(today);
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 handleTimeSlotClick = ({
slotStart,
slotEnd,
resourceId,
}: {
slotStart: string;
slotEnd: string;
nativeEvent: React.MouseEvent<HTMLButtonElement>;
resourceId?: string | number;
}) => {
setSelectedResourceId(resourceId ? String(resourceId) : null);
setSelectedEventData({
title: '',
start: new Date(slotStart),
end: new Date(slotEnd),
color: 'blue',
});
setFormOpened(true);
};
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 (
<>
<ResourcesSchedule
date={date}
onDateChange={setDate}
resources={resources}
events={events}
withDragSlotSelect
onTimeSlotClick={handleTimeSlotClick}
onDayClick={handleDayClick}
onSlotDragEnd={handleSlotDragEnd}
onEventClick={handleEventClick}
dayViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollTime: '08:00:00' }}
weekViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollDateTime: `${today} 08:00:00` }}
monthViewProps={{ startScrollDate: today }}
/>
<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>
</>
);
}External drag and drop
You can drag external items onto the schedule using onExternalEventDrop prop. The callback
receives the resourceId of the target resource.
Drag to schedule
Quick Sync
30 min
Workshop
120 min
One-on-One
60 min
import { useRef, useState } from 'react';
import dayjs from 'dayjs';
import { Box, Grid, Text } from '@mantine/core';
import { ResourcesSchedule, ScheduleEventData } from '@mantine/schedule';
const resources = [
{ id: 'tokyo', label: 'Meeting room: Tokyo' },
{ id: 'paris', label: 'Meeting room: Paris' },
{ id: 'new-york', label: 'Meeting room: New York' },
{ id: 'london', label: 'Meeting room: London' },
];
const sidebarItems = [
{ title: 'Quick Sync', duration: 30, color: 'teal' },
{ title: 'Workshop', duration: 120, color: 'orange' },
{ title: 'One-on-One', duration: 60, color: 'violet' },
];
function Demo() {
const today = dayjs().format('YYYY-MM-DD');
const [date, setDate] = useState(today);
const [events, setEvents] = useState<ScheduleEventData[]>([]);
const nextId = useRef(1);
const handleExternalDrop = ({
dataTransfer,
dropDateTime,
resourceId,
}: {
dataTransfer: DataTransfer;
dropDateTime: string;
resourceId?: string | number;
}) => {
const raw = dataTransfer.getData('text/plain');
if (!raw) {
return;
}
const data = JSON.parse(raw);
const start = dayjs(dropDateTime);
const end = start.add(data.duration, 'minutes');
setEvents((prev) => [
...prev,
{
id: nextId.current++,
title: data.title,
start: start.format('YYYY-MM-DD HH:mm:ss'),
end: end.format('YYYY-MM-DD HH:mm:ss'),
color: data.color,
resourceId,
},
]);
};
const handleEventDrop = ({
eventId,
newStart,
newEnd,
resourceId,
}: {
eventId: string | number;
newStart: string;
newEnd: string;
event: ScheduleEventData;
resourceId?: string | number;
}) => {
setEvents((prev) =>
prev.map((event) =>
event.id === eventId ? { ...event, start: newStart, end: newEnd, resourceId } : event
)
);
};
return (
<Grid>
<Grid.Col span={{ base: 12, sm: 3 }}>
<Text fw={500} mb="xs">Drag to schedule</Text>
{sidebarItems.map((item) => (
<Box
key={item.title}
draggable
onDragStart={(e) => {
e.dataTransfer.setData('text/plain', JSON.stringify(item));
e.dataTransfer.effectAllowed = 'copy';
}}
style={{
padding: '8px 12px',
marginBottom: 8,
borderRadius: 4,
cursor: 'grab',
backgroundColor: `var(--mantine-color-${item.color}-light)`,
color: `var(--mantine-color-${item.color}-light-color)`,
}}
>
<Text size="sm" fw={500}>{item.title}</Text>
<Text size="xs">{item.duration} min</Text>
</Box>
))}
</Grid.Col>
<Grid.Col span={{ base: 12, sm: 9 }}>
<ResourcesSchedule
date={date}
onDateChange={setDate}
resources={resources}
events={events}
dayViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollTime: '08:00:00' }}
weekViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollDateTime: `${today} 08:00:00` }}
monthViewProps={{ startScrollDate: today }}
withEventsDragAndDrop
onEventDrop={handleEventDrop}
onExternalEventDrop={handleExternalDrop}
/>
</Grid.Col>
</Grid>
);
}Event resize
Enable event resizing with withEventResize prop. Event resize is only available in day view
where events can be resized by dragging their left or right edges.
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesSchedule, ScheduleEventData } from '@mantine/schedule';
import { events as initialEvents, resources } from './data';
function Demo() {
const today = dayjs().format('YYYY-MM-DD');
const [date, setDate] = useState(today);
const [events, setEvents] = useState<ScheduleEventData[]>(initialEvents);
const handleEventUpdate = ({ eventId, newStart, newEnd }: { eventId: string | number; newStart: string; newEnd: string; event: ScheduleEventData }) => {
setEvents((prev) =>
prev.map((event) =>
event.id === eventId ? { ...event, start: newStart, end: newEnd } : event
)
);
};
return (
<ResourcesSchedule
date={date}
onDateChange={setDate}
resources={resources}
events={events}
withEventsDragAndDrop
onEventDrop={({ eventId, newStart, newEnd, resourceId }) => {
setEvents((prev) =>
prev.map((event) =>
event.id === eventId
? { ...event, start: newStart, end: newEnd, resourceId }
: event
)
);
}}
withEventResize
onEventResize={handleEventUpdate}
dayViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollTime: '08:00:00' }}
weekViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollDateTime: `${today} 08:00:00` }}
monthViewProps={{ startScrollDate: today }}
/>
);
}Static mode
Set mode="static" to disable all interactions across all three views.
import dayjs from 'dayjs';
import { useState } from 'react';
import { ResourcesSchedule } from '@mantine/schedule';
import { events, resources } from './data';
function Demo() {
const today = dayjs().format('YYYY-MM-DD');
const [date, setDate] = useState(today);
return (
<ResourcesSchedule
date={date}
onDateChange={setDate}
resources={resources}
events={events}
mode="static"
dayViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollTime: '08:00:00' }}
weekViewProps={{ startTime: '08:00:00', endTime: '18:00:00', startScrollDateTime: `${today} 08:00:00` }}
monthViewProps={{ startScrollDate: today }}
/>
);
}