React Timeline Employee Shift Slots
Get started with this exampleUsage
theme="ios" You can turn auto theming on and let it decide based on the platform.
Usage
themeVariant="light" You can turn auto dark mode on based on system settings.
Usage
clickToCreate={true}
// click to create is on dragToCreate={true}
// drag to create is on dragToMove={true}
// reordering is on dragToResize={true}
// resizing is on eventDelete={true}
// deleting is on Fine-tune drag & drop with these options, which are false by default.
Usage
locale={localeEn} Selecting the locale loads the localized settings and the translated UI text.
Employee Shift Slots
Use the renderSlot option for rendering a custom time slot header. Customize how the time slots look and what they show. Utilize properties passed in the slots array.
Demo

Code
View code on GitHub
import { Eventcalendar, formatDate, setOptions } from '@mobiscroll/react';
import { useCallback, useMemo, useState } from 'react';
setOptions({
locale: ,
theme: ' ',
themeVariant: ' '
});
const staff = [
{
id: 1,
name: 'Ryan',
color: '#e20000',
title: 'Cloud System Engineer',
img: 'https://img.mobiscroll.com/demos/m1.png',
},
{
id: 2,
name: 'Kate',
color: '#60e81a',
title: 'Help Desk Specialist',
img: 'https://img.mobiscroll.com/demos/f1.png',
},
{
id: 3,
name: 'John',
color: '#3ba7ff',
title: 'Application Developer',
img: 'https://img.mobiscroll.com/demos/m2.png',
},
];
const defaultShifts = [
{
start: '2026-11-06T07:00',
end: '2026-11-06T13:00',
title: '07:00 - 13:00',
resource: 2,
slot: 1,
},
{
start: '2026-11-06T07:00',
end: '2026-11-06T13:00',
title: '07:00 - 13:00',
resource: 3,
slot: 1,
},
{
start: '2026-11-06T07:00',
end: '2026-11-06T13:00',
title: '07:00 - 13:00',
resource: 6,
slot: 1,
},
{
start: '2026-11-07T07:00',
end: '2026-11-07T13:00',
title: '07:00 - 13:00',
resource: 1,
slot: 1,
},
{
start: '2026-11-07T07:00',
end: '2026-11-07T13:00',
title: '07:00 - 13:00',
resource: 2,
slot: 1,
},
{
start: '2026-11-07T12:00',
end: '2026-11-07T18:00',
title: '12:00 - 18:00',
resource: 3,
slot: 2,
},
{
start: '2026-11-08T07:00',
end: '2026-11-08T13:00',
title: '07:00 - 13:00',
resource: 1,
slot: 1,
},
{
start: '2026-11-08T07:00',
end: '2026-11-08T13:00',
title: '07:00 - 13:00',
resource: 3,
slot: 1,
},
{
start: '2026-11-08T12:00',
end: '2026-11-08T18:00',
title: '12:00 - 18:00',
resource: 2,
slot: 2,
},
{
start: '2026-11-09T12:00',
end: '2026-11-09T18:00',
title: '12:00 - 18:00',
resource: 2,
slot: 2,
},
{
start: '2026-11-10T07:00',
end: '2026-11-10T13:00',
title: '07:00 - 13:00',
resource: 1,
slot: 1,
},
{
start: '2026-11-10T12:00',
end: '2026-11-10T18:00',
title: '12:00 - 18:00',
resource: 2,
slot: 2,
},
{
start: '2026-11-10T12:00',
end: '2026-11-10T18:00',
title: '12:00 - 18:00',
resource: 3,
slot: 2,
},
{
start: '2026-11-11T07:00',
end: '2026-11-11T13:00',
title: '07:00 - 13:00',
resource: 1,
slot: 1,
},
{
start: '2026-11-11T12:00',
end: '2026-11-11T18:00',
title: '12:00 - 18:00',
resource: 3,
slot: 2,
},
];
const myColors = [
{
background: '#a5ceff4d',
slot: 1,
recurring: {
repeat: 'weekly',
weekDays: 'MO,TU,WE,TH,FR',
},
},
{
background: '#f7f7bb4d',
slot: 2,
recurring: {
repeat: 'weekly',
weekDays: 'MO,TU,WE,TH,FR',
},
},
];
const mySlots = [
{
id: 1,
name: 'Morning',
},
{
id: 2,
name: 'Afternoon',
},
];
function App() {
const [shifts, setShifts] = useState(defaultShifts);
const myView = useMemo(
() => ({
timeline: {
type: 'week',
eventDisplay: 'fill',
startDay: 1,
endDay: 5,
},
}),
[],
);
const renderMySlot = useCallback((args) => {
const slotId = args.slot.id;
return (
<div style={{ backgroundColor: slotId === 1 ? '#a5ceff4d' : '#f7f7bb4d', padding: '4px' }}>
<div className="slot-name">{args.slot.name}</div>
<div className="slot-time">{slotId === 1 ? '7am - 1pm' : '12pm - 6pm'}</div>
</div>
);
}, []);
const extendMyDefaultEvent = useCallback((ev) => {
const d = ev.start;
const start = new Date(d.getFullYear(), d.getMonth(), d.getDate(), ev.slot === 1 ? 7 : 12);
const end = new Date(d.getFullYear(), d.getMonth(), d.getDate(), ev.slot === 1 ? 13 : 18);
return {
title: formatDate('HH:mm', start) + ' - ' + formatDate('HH:mm', end),
start,
end,
};
}, []);
const handleEventUpdate = useCallback(
(args) => {
const event = args.event;
const index = shifts.findIndex((x) => x.id === event.id);
const newShifts = [...shifts];
newShifts.splice(index, 1, { ...event, title: event.slot === 1 ? '07:00 - 13:00' : '12:00 - 18:00' });
setShifts(newShifts);
return false;
},
[shifts],
);
return (
<Eventcalendar
view={myView}
data={shifts}
resources={staff}
colors={myColors}
slots={mySlots}
clickToCreate={true}
dragToMove={true}
dragToResize={false}
renderSlot={renderMySlot}
onEventUpdate={handleEventUpdate}
extendDefaultEvent={extendMyDefaultEvent}
cssClass="md-shift-template"
/>
);
}
export default App;
import {
Eventcalendar,
formatDate,
MbscCalendarColor,
MbscCalendarEvent,
MbscEventcalendarView,
MbscEventUpdateEvent,
MbscNewEventData,
MbscResource,
MbscSlot,
MbscSlotData,
setOptions ,
} from '@mobiscroll/react';
import { useCallback, useMemo, useState } from 'react';
setOptions({
locale: ,
theme: ' ',
themeVariant: ' '
});
const staff: MbscResource[] = [
{
id: 1,
name: 'Ryan',
color: '#e20000',
title: 'Cloud System Engineer',
img: 'https://img.mobiscroll.com/demos/m1.png',
},
{
id: 2,
name: 'Kate',
color: '#60e81a',
title: 'Help Desk Specialist',
img: 'https://img.mobiscroll.com/demos/f1.png',
},
{
id: 3,
name: 'John',
color: '#3ba7ff',
title: 'Application Developer',
img: 'https://img.mobiscroll.com/demos/m2.png',
},
];
const defaultShifts: MbscCalendarEvent[] = [
{
start: '2026-11-06T07:00',
end: '2026-11-06T13:00',
title: '07:00 - 13:00',
resource: 2,
slot: 1,
},
{
start: '2026-11-06T07:00',
end: '2026-11-06T13:00',
title: '07:00 - 13:00',
resource: 3,
slot: 1,
},
{
start: '2026-11-06T07:00',
end: '2026-11-06T13:00',
title: '07:00 - 13:00',
resource: 6,
slot: 1,
},
{
start: '2026-11-07T07:00',
end: '2026-11-07T13:00',
title: '07:00 - 13:00',
resource: 1,
slot: 1,
},
{
start: '2026-11-07T07:00',
end: '2026-11-07T13:00',
title: '07:00 - 13:00',
resource: 2,
slot: 1,
},
{
start: '2026-11-07T12:00',
end: '2026-11-07T18:00',
title: '12:00 - 18:00',
resource: 3,
slot: 2,
},
{
start: '2026-11-08T07:00',
end: '2026-11-08T13:00',
title: '07:00 - 13:00',
resource: 1,
slot: 1,
},
{
start: '2026-11-08T07:00',
end: '2026-11-08T13:00',
title: '07:00 - 13:00',
resource: 3,
slot: 1,
},
{
start: '2026-11-08T12:00',
end: '2026-11-08T18:00',
title: '12:00 - 18:00',
resource: 2,
slot: 2,
},
{
start: '2026-11-09T12:00',
end: '2026-11-09T18:00',
title: '12:00 - 18:00',
resource: 2,
slot: 2,
},
{
start: '2026-11-10T07:00',
end: '2026-11-10T13:00',
title: '07:00 - 13:00',
resource: 1,
slot: 1,
},
{
start: '2026-11-10T12:00',
end: '2026-11-10T18:00',
title: '12:00 - 18:00',
resource: 2,
slot: 2,
},
{
start: '2026-11-10T12:00',
end: '2026-11-10T18:00',
title: '12:00 - 18:00',
resource: 3,
slot: 2,
},
{
start: '2026-11-11T07:00',
end: '2026-11-11T13:00',
title: '07:00 - 13:00',
resource: 1,
slot: 1,
},
{
start: '2026-11-11T12:00',
end: '2026-11-11T18:00',
title: '12:00 - 18:00',
resource: 3,
slot: 2,
},
];
const myColors: MbscCalendarColor[] = [
{
background: '#a5ceff4d',
slot: 1,
recurring: {
repeat: 'weekly',
weekDays: 'MO,TU,WE,TH,FR',
},
},
{
background: '#f7f7bb4d',
slot: 2,
recurring: {
repeat: 'weekly',
weekDays: 'MO,TU,WE,TH,FR',
},
},
];
const mySlots: MbscSlot[] = [
{
id: 1,
name: 'Morning',
},
{
id: 2,
name: 'Afternoon',
},
];
function App() {
const [shifts, setShifts] = useState<MbscCalendarEvent[]>(defaultShifts);
const myView = useMemo<MbscEventcalendarView>(
() => ({
timeline: {
type: 'week',
eventDisplay: 'fill',
startDay: 1,
endDay: 5,
},
}),
[],
);
const renderMySlot = (args: MbscSlotData) => {
const slotId = args.slot.id;
return (
<div style={{ backgroundColor: slotId === 1 ? '#a5ceff4d' : '#f7f7bb4d', padding: '4px' }}>
<div className="slot-name">{args.slot.name}</div>
<div className="slot-time">{slotId === 1 ? '7am - 1pm' : '12pm - 6pm'}</div>
</div>
);
};
const extendMyDefaultEvent = useCallback((ev: MbscNewEventData) => {
const d = ev.start;
const start = new Date(d.getFullYear(), d.getMonth(), d.getDate(), ev.slot === 1 ? 7 : 12);
const end = new Date(d.getFullYear(), d.getMonth(), d.getDate(), ev.slot === 1 ? 13 : 18);
return {
title: formatDate('HH:mm', start) + ' - ' + formatDate('HH:mm', end),
start,
end,
};
}, []);
const handleEventUpdate = useCallback(
(args: MbscEventUpdateEvent) => {
const event = args.event;
const index = shifts.findIndex((x) => x.id === event.id);
const newShifts = [...shifts];
newShifts.splice(index, 1, { ...event, title: event.slot === 1 ? '07:00 - 13:00' : '12:00 - 18:00' });
setShifts(newShifts);
return false;
},
[shifts],
);
return (
<Eventcalendar
view={myView}
data={shifts}
resources={staff}
colors={myColors}
slots={mySlots}
clickToCreate={true}
dragToMove={true}
dragToResize={false}
renderSlot={renderMySlot}
onEventUpdate={handleEventUpdate}
extendDefaultEvent={extendMyDefaultEvent}
cssClass="md-shift-template"
/>
);
}
export default App;
.slot-name {
line-height: 20px;
font-size: 15px;
}
.slot-time {
opacity: 0.5;
line-height: 20px;
font-size: 13px;
}
.md-shift-template .mbsc-timeline-header-placeholder {
height: 74px;
}
.md-shift-template .mbsc-timeline-slots {
height: 48px;
text-align: center;
}
.md-shift-template .mbsc-schedule-event {
display: flex;
align-items: center;
justify-content: center;
height: 36px;
}
.md-shift-template .mbsc-timeline-slot-title {
padding: 0;
}