---
title: "React Scheduler Doctors Availability Example | Mobiscroll"
component: "Scheduler"
framework: "React"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/react/scheduler/doctors-availability-calendar-planner"
---

# Doctors Availability - React Scheduler demo

Display per-doctor availability in React Scheduler — color-coded tracks mark open slots per doctor, automatically respecting working hours. For React and Next.JS.

- [Live demo](https://demo.mobiscroll.com/react/scheduler/doctors-availability-calendar-planner)
- [View React JS code on GitHub](https://github.com/acidb/mobiscroll-demos-react/tree/main/src/demos/eventcalendar/scheduler/doctors-availability-calendar-planner)
- [View React TS code on GitHub](https://github.com/acidb/mobiscroll-demos-react-ts/tree/main/src/demos/eventcalendar/scheduler/doctors-availability-calendar-planner)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/javascript/scheduler/doctors-availability-calendar-planner.md), [jQuery](https://demo.mobiscroll.com/jquery/scheduler/doctors-availability-calendar-planner.md), [Angular](https://demo.mobiscroll.com/angular/scheduler/doctors-availability-calendar-planner.md), [Vue](https://demo.mobiscroll.com/vue/scheduler/doctors-availability-calendar-planner.md)

## Description

You can tailor the scheduler to show availability per doctor using visual indicators in each column. Availability is marked with a green status track on the left side of each doctor's column, making open slots easy to spot and supporting quick scheduling decisions.

To create the visual indicator, we added extra resource columns with a narrow width, styled using a custom CSS class set via the `cssClass` property in the resource data. The availability track is shown using events placed on these columns, with their content hidden via [`renderSchedulerEvent` option](https://mobiscroll.com/docs/react/eventcalendar/api#renderer-renderSchedulerEvent) and resized responsively based on the narrow resource column width. Time ranges outside each doctor's working hours are disabled to reflect their actual schedule.

## What this demo shows

- A desktop-style work-week scheduler with a fixed Monday-to-Friday header, repeated doctor resources for each day, and a scrollable 10-hour time grid for the selected week.
- **Header navigation** The month and year label in the top left opens date navigation, while the blue previous and next arrows and the Today button on the right make it easy to move between weeks and jump back to the current day.
- **Week view** The fixed strip below the header displays the days of the selected week, with the current date highlighted by a blue circle.
- **Resources** Each day repeats the same doctor resources, showing an avatar, the doctor's name in bold, and the doctor's profession below the name.
- **Resource columns**: Each resource has its own column inside the day column.
- **Doctor availability** Availability is marked with a green status track on the left side of each doctor's column, making open slots easier to identify.
- **Time grid**: The scheduler displays time from 8 AM to 6 PM.
- **Scrolling behavior**: The scheduler can be scrolled vertically through the time grid and horizontally across the days of the month and their resource columns.
- **Timed events** Timed events are displayed in the weekly scheduler grid as colored cards with a colored stripe on the left, the event title in bold, and the exact start and end time displayed above the title.
- **Event overlapping** Events cannot overlap in the scheduler grid.
- **Current time** A blue current-time line appears across the time grid.
- **Disabled hours** Each doctor has individual working hours. Time ranges outside those hours are disabled and shown with a gray background and block event creation, drag-to-move, and drag-to-resize interactions.
- **Hover behavior** Hovering over the time grid shows a time indicator that follows the cursor in 15-minute increments.
- **Event interaction** Hovering over events highlights them and shows drag and resize handles for repositioning or changing duration.
- **Event selection** Clicking an event selects and highlights it.
- **Event creation** In available time ranges, double-clicking the grid or dragging across a time range creates a new appointment and shows a bottom-center toast with the message `Appointment created`.
- **Event deletion** Removing an event from the scheduler shows a bottom-center toast with the message `Appointment deleted`.
- **Event update** Updateing an appointment from the scheduler shows a bottom-center toast with the message `Appointment updated`.

## Best for

- **Healthcare and medical practices** Schedule patients across doctors, specialists, therapists, nurses, or treatment rooms while keeping provider availability visible.
- **Fitness and wellness centers** Manage appointments for trainers, instructors, physiotherapists, massage therapists, and treatment rooms.
- **Beauty salons and spas** Assign customers to stylists, beauticians, therapists, and service stations throughout the day.
- **Professional services and consulting firms** Schedule client meetings, consultations, coaching sessions, legal appointments, or financial advisory meetings across multiple consultants.
- **Educational institutions and tutoring centers** Coordinate classes, tutoring sessions, instructors, training rooms, and student appointments.
- **Recruitment and HR teams** Manage candidate interviews across recruiters, hiring managers, interview panels, and meeting rooms.
- **Field service and maintenance operations** Schedule technicians, inspectors, engineers, or repair specialists and monitor daily workloads.
- **Customer support and service centers** Allocate appointments, consultations, onboarding sessions, or customer meetings across available agents and specialists.
- **Equipment and facility booking systems** Manage reservations for meeting rooms, studios, workspaces, vehicles, machinery, or shared equipment.
- **Legal, accounting, and financial firms** Coordinate client appointments, consultations, audits, reviews, and advisory sessions across professionals.
- **Real estate agencies** Schedule property viewings, inspections, appraisals, and client meetings across multiple agents.
- **Creative agencies and production teams** Organize project reviews, design consultations, photo shoots, studio bookings, and production resources.
- **Higher education and research organizations** Schedule faculty office hours, lab sessions, equipment usage, academic advising, and research facilities.

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-themeVariant) - option
- [`cssClass`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-cssClass) - option
- [`clickToCreate`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-clickToCreate) - option
- [`dragToCreate`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-dragToCreate) - option
- [`dragToMove`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-dragToMove) - option
- [`dragToResize`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-dragToResize) - option
- [`eventDelete`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-eventDelete) - option
- [`data`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-data) - option
- [`dragTimeStep`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-dragTimeStep) - option
- [`eventOverlap`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-eventOverlap) - option
- [`groupBy`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-groupBy) - option
- [`invalid`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-invalid) - option
- [`resources`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-resources) - option
- [`view`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-view) - option
- [`renderSchedulerEventContent`](https://mobiscroll.com/docs/react/eventcalendar/api#renderer-renderSchedulerEventContent) - renderer
- [`renderResource`](https://mobiscroll.com/docs/react/eventcalendar/api#renderer-renderResource) - renderer
- [`onEventCreated`](https://mobiscroll.com/docs/react/eventcalendar/api#event-onEventCreated) - event
- [`onEventDeleted`](https://mobiscroll.com/docs/react/eventcalendar/api#event-onEventDeleted) - event
- [`onEventUpdated`](https://mobiscroll.com/docs/react/eventcalendar/api#event-onEventUpdated) - event
- [`onClose`](https://mobiscroll.com/docs/react/notifications/toast#event-onClose) - event

## Code

### JSX

```jsx
import { Eventcalendar, setOptions, Toast } from '@mobiscroll/react';
import { useCallback, useMemo, useState } from 'react';

setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

function App() {
  const [isToastOpen, setToastOpen] = useState(false);
  const [toastText, setToastText] = useState();

  const myEvents = useMemo(
    () => [
      {
        id: 1,
        resource: 2,
        tooltip: 'Availability: 10:00 - 18:00',
        start: '10:00',
        end: '18:00',
        recurring: { repeat: 'daily' },
        type: 'availability',
        editable: false,
      },
      {
        id: 2,
        resource: 4,
        tooltip: 'Availability: 08:00 - 16:00',
        start: '08:00',
        end: '16:00',
        recurring: { repeat: 'daily' },
        type: 'availability',
        editable: false,
      },
      {
        id: 3,
        resource: 6,
        tooltip: 'Availability: 09:00 - 17:00',
        start: '09:00',
        end: '17:00',
        recurring: { repeat: 'daily' },
        type: 'availability',
        editable: false,
      },
      {
        id: 4,
        resource: 8,
        tooltip: 'Availability: 10:00 - 18:00',
        start: '10:00',
        end: '18:00',
        recurring: { repeat: 'daily' },
        type: 'availability',
        editable: false,
      },
      // Showing partial data. Download full source.
    ],
    [],
  );

  const myResources = useMemo(
    () => [
      {
        id: 2,
        color: 'rgb(40, 179, 114)',
        cssClass: 'mds-healthcare-res-col-bar',
      },
      {
        id: 1,
        name: 'Dr. James Cole',
        color: 'rgba(255, 204, 193)',
        cssClass: 'mds-healthcare-res-col',
        description: 'Injury Recovery Specialist',
        img: 'https://img.mobiscroll.com/demos/m4.png',
      },
      {
        id: 4,
        color: 'rgb(40, 179, 114)',
        eventCreation: false,
        cssClass: 'mds-healthcare-res-col-bar',
      },
      {
        id: 3,
        name: 'Dr. Anna Hayes',
        color: 'rgb(193, 221, 195)',
        cssClass: 'mds-healthcare-res-col',
        description: 'Sports Physiotherapist',
        img: 'https://img.mobiscroll.com/demos/f3.png',
      },
      {
        id: 6,
        color: 'rgb(40, 179, 114)',
        eventCreation: false,
        cssClass: 'mds-healthcare-res-col-bar',
      },
      {
        id: 5,
        name: 'Dr. Mark Lewis',
        color: 'rgb(134, 224, 193)',
        cssClass: 'mds-healthcare-res-col',
        description: 'Mobility Recovery Expert',
        img: 'https://img.mobiscroll.com/demos/m3.png',
      },
      {
        id: 8,
        color: 'rgb(40, 179, 114)',
        eventCreation: false,
        cssClass: 'mds-healthcare-res-col-bar',
      },
      {
        id: 7,
        name: 'Dr. Emily Carter',
        color: 'rgb(255, 193, 228)',
        cssClass: 'mds-healthcare-res-col',
        description: 'Chiropractic Specialist',
        img: 'https://img.mobiscroll.com/demos/f1.png',
      },
      {
        id: 10,
        color: 'rgb(40, 179, 114)',
        eventCreation: false,
        cssClass: 'mds-healthcare-res-col-bar',
      },
      {
        id: 9,
        name: 'Dr. Robert Stone',
        color: 'rgb(193, 204, 255)',
        cssClass: 'mds-healthcare-res-col',
        description: 'Orthopedic Surgeon',
        img: 'https://img.mobiscroll.com/demos/m2.png',
      },
      {
        id: 12,
        color: 'rgb(40, 179, 114)',
        eventCreation: false,
        cssClass: 'mds-healthcare-res-col-bar',
      },
      {
        id: 11,
        name: 'Dr. Sophia Miller',
        color: 'rgb(255, 223, 176)',
        cssClass: 'mds-healthcare-res-col',
        description: 'Sports Physiotherapist',
        img: 'https://img.mobiscroll.com/demos/f2.png',
      },
    ],
    [],
  );

  const myInvalids = useMemo(
    () => [
      { resource: [1, 2], start: '00:00', end: '10:00', recurring: { repeat: 'daily' } },
      { resource: [1, 2], start: '18:00', end: '24:00', recurring: { repeat: 'daily' } },
      { resource: [3, 4], start: '00:00', end: '08:00', recurring: { repeat: 'daily' } },
      { resource: [3, 4], start: '16:00', end: '24:00', recurring: { repeat: 'daily' } },
      { resource: [5, 6], start: '00:00', end: '09:00', recurring: { repeat: 'daily' } },
      { resource: [5, 6], start: '17:00', end: '24:00', recurring: { repeat: 'daily' } },
      { resource: [7, 8], start: '00:00', end: '10:00', recurring: { repeat: 'daily' } },
      { resource: [7, 8], start: '18:00', end: '24:00', recurring: { repeat: 'daily' } },
      { resource: [9, 10], start: '00:00', end: '08:00', recurring: { repeat: 'daily' } },
      { resource: [9, 10], start: '16:00', end: '24:00', recurring: { repeat: 'daily' } },
      { resource: [11, 12], start: '00:00', end: '09:00', recurring: { repeat: 'daily' } },
      { resource: [11, 12], start: '17:00', end: '24:00', recurring: { repeat: 'daily' } },
    ],
    [],
  );

  const myView = useMemo(
    () => ({
      scheduler: {
        type: 'week',
        startDay: 1,
        endDay: 5,
        startTime: '08:00',
        endTime: '18:00',
        timeCellStep: 20,
        timeLabelStep: 20,
        allDay: false,
      },
    }),
    [],
  );

  const customScheduleEventContent = useCallback((event) => {
    if (!event.original.type) {
      return <>Patient: {event.title === 'New event' ? 'John Doe' : event.title}</>;
    }
    return null;
  }, []);

  const customResource = useCallback(
    (resource) => (
      <>
        {resource.cssClass === 'mds-healthcare-res-col' && (
          <div className="mbsc-flex">
            <img className="mds-healthcare-res-avatar mbsc-flex-none" alt={resource.name} src={resource.img} />
            <div className="mds-healthcare-res-cont mbsc-flex-1-1">
              <div className="mds-healthcare-res-name">{resource.name}</div>
              <div className="mds-healthcare-res-desc">{resource.description}</div>
            </div>
          </div>
        )}
      </>
    ),
    [],
  );

  const showToast = useCallback((message) => {
    setToastText(message);
    setToastOpen(true);
  }, []);

  const handleEventCreated = useCallback(() => {
    showToast('Appointment created');
  }, [showToast]);

  const handleEventUpdated = useCallback(() => {
    showToast('Appointment updated');
  }, [showToast]);

  const handleEventDeleted = useCallback(() => {
    showToast('Appointment deleted');
  }, [showToast]);

  const handleToastClose = useCallback(() => {
    setToastOpen(false);
  }, []);

  return (
    <>
      <Eventcalendar
        clickToCreate={true}
        dragToCreate={true}
        dragToMove={true}
        dragToResize={true}
        eventDelete={true}
        cssClass="mds-healthcare"
        data={myEvents}
        dragTimeStep={20}
        eventOverlap={false}
        groupBy="date"
        invalid={myInvalids}
        resources={myResources}
        view={myView}
        renderSchedulerEventContent={customScheduleEventContent}
        renderResource={customResource}
        onEventCreated={handleEventCreated}
        onEventDeleted={handleEventDeleted}
        onEventUpdated={handleEventUpdated}
      />
      <Toast message={toastText} isOpen={isToastOpen} onClose={handleToastClose} />
    </>
  );
}

export default App;
```

### TSX

```tsx
import {
  Eventcalendar,
  MbscCalendarEvent,
  MbscCalendarEventData,
  MbscEventcalendarView,
  MbscResource,
  setOptions,
  Toast,
} from '@mobiscroll/react';
import { FC, useCallback, useMemo, useState } from 'react';

setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

const App: FC = () => {
  const [isToastOpen, setToastOpen] = useState(false);
  const [toastText, setToastText] = useState('');

  const myEvents = useMemo<MbscCalendarEvent[]>(
    () => [
      {
        id: 1,
        resource: 2,
        tooltip: 'Availability: 10:00 - 18:00',
        start: '10:00',
        end: '18:00',
        recurring: { repeat: 'daily' },
        type: 'availability',
        editable: false,
      },
      {
        id: 2,
        resource: 4,
        tooltip: 'Availability: 08:00 - 16:00',
        start: '08:00',
        end: '16:00',
        recurring: { repeat: 'daily' },
        type: 'availability',
        editable: false,
      },
      {
        id: 3,
        resource: 6,
        tooltip: 'Availability: 09:00 - 17:00',
        start: '09:00',
        end: '17:00',
        recurring: { repeat: 'daily' },
        type: 'availability',
        editable: false,
      },
      {
        id: 4,
        resource: 8,
        tooltip: 'Availability: 10:00 - 18:00',
        start: '10:00',
        end: '18:00',
        recurring: { repeat: 'daily' },
        type: 'availability',
        editable: false,
      },
      // Showing partial data. Download full source.
    ],
    [],
  );

  const myResources = useMemo<MbscResource[]>(
    () => [
      {
        id: 2,
        color: 'rgb(40, 179, 114)',
        cssClass: 'mds-healthcare-res-col-bar',
      },
      {
        id: 1,
        name: 'Dr. James Cole',
        color: 'rgba(255, 204, 193)',
        cssClass: 'mds-healthcare-res-col',
        description: 'Injury Recovery Specialist',
        img: 'https://img.mobiscroll.com/demos/m4.png',
      },
      {
        id: 4,
        color: 'rgb(40, 179, 114)',
        eventCreation: false,
        cssClass: 'mds-healthcare-res-col-bar',
      },
      {
        id: 3,
        name: 'Dr. Anna Hayes',
        color: 'rgb(193, 221, 195)',
        cssClass: 'mds-healthcare-res-col',
        description: 'Sports Physiotherapist',
        img: 'https://img.mobiscroll.com/demos/f3.png',
      },
      {
        id: 6,
        color: 'rgb(40, 179, 114)',
        eventCreation: false,
        cssClass: 'mds-healthcare-res-col-bar',
      },
      {
        id: 5,
        name: 'Dr. Mark Lewis',
        color: 'rgb(134, 224, 193)',
        cssClass: 'mds-healthcare-res-col',
        description: 'Mobility Recovery Expert',
        img: 'https://img.mobiscroll.com/demos/m3.png',
      },
      {
        id: 8,
        color: 'rgb(40, 179, 114)',
        eventCreation: false,
        cssClass: 'mds-healthcare-res-col-bar',
      },
      {
        id: 7,
        name: 'Dr. Emily Carter',
        color: 'rgb(255, 193, 228)',
        cssClass: 'mds-healthcare-res-col',
        description: 'Chiropractic Specialist',
        img: 'https://img.mobiscroll.com/demos/f1.png',
      },
      {
        id: 10,
        color: 'rgb(40, 179, 114)',
        eventCreation: false,
        cssClass: 'mds-healthcare-res-col-bar',
      },
      {
        id: 9,
        name: 'Dr. Robert Stone',
        color: 'rgb(193, 204, 255)',
        cssClass: 'mds-healthcare-res-col',
        description: 'Orthopedic Surgeon',
        img: 'https://img.mobiscroll.com/demos/m2.png',
      },
      {
        id: 12,
        color: 'rgb(40, 179, 114)',
        eventCreation: false,
        cssClass: 'mds-healthcare-res-col-bar',
      },
      {
        id: 11,
        name: 'Dr. Sophia Miller',
        color: 'rgb(255, 223, 176)',
        cssClass: 'mds-healthcare-res-col',
        description: 'Sports Physiotherapist',
        img: 'https://img.mobiscroll.com/demos/f2.png',
      },
    ],
    [],
  );

  const myInvalids = useMemo(
    () => [
      { resource: [1, 2], start: '00:00', end: '10:00', recurring: { repeat: 'daily' } },
      { resource: [1, 2], start: '18:00', end: '24:00', recurring: { repeat: 'daily' } },
      { resource: [3, 4], start: '00:00', end: '08:00', recurring: { repeat: 'daily' } },
      { resource: [3, 4], start: '16:00', end: '24:00', recurring: { repeat: 'daily' } },
      { resource: [5, 6], start: '00:00', end: '09:00', recurring: { repeat: 'daily' } },
      { resource: [5, 6], start: '17:00', end: '24:00', recurring: { repeat: 'daily' } },
      { resource: [7, 8], start: '00:00', end: '10:00', recurring: { repeat: 'daily' } },
      { resource: [7, 8], start: '18:00', end: '24:00', recurring: { repeat: 'daily' } },
      { resource: [9, 10], start: '00:00', end: '08:00', recurring: { repeat: 'daily' } },
      { resource: [9, 10], start: '16:00', end: '24:00', recurring: { repeat: 'daily' } },
      { resource: [11, 12], start: '00:00', end: '09:00', recurring: { repeat: 'daily' } },
      { resource: [11, 12], start: '17:00', end: '24:00', recurring: { repeat: 'daily' } },
    ],
    [],
  );

  const myView = useMemo<MbscEventcalendarView>(
    () => ({
      scheduler: {
        type: 'week',
        startDay: 1,
        endDay: 5,
        startTime: '08:00',
        endTime: '18:00',
        timeCellStep: 20,
        timeLabelStep: 20,
        allDay: false,
      },
    }),
    [],
  );

  const customScheduleEventContent = useCallback((event: MbscCalendarEventData) => {
    if (!event.original!.type) {
      return <div>Patient: {event.title === 'New event' ? 'John Doe' : event.title}</div>;
    }
    return null;
  }, []);

  const customResource = useCallback(
    (resource: MbscResource) => (
      <>
        {resource.cssClass === 'mds-healthcare-res-col' && (
          <div className="mbsc-flex">
            <img className="mds-healthcare-res-avatar mbsc-flex-none" alt={resource.name} src={resource.img} />
            <div className="mds-healthcare-res-cont mbsc-flex-1-1">
              <div className="mds-healthcare-res-name">{resource.name}</div>
              <div className="mds-healthcare-res-desc">{resource.description}</div>
            </div>
          </div>
        )}
      </>
    ),
    [],
  );

  const showToast = useCallback((message: string) => {
    setToastText(message);
    setToastOpen(true);
  }, []);

  const handleEventCreated = useCallback(() => {
    showToast('Appointment created');
  }, [showToast]);

  const handleEventUpdated = useCallback(() => {
    showToast('Appointment updated');
  }, [showToast]);

  const handleEventDeleted = useCallback(() => {
    showToast('Appointment deleted');
  }, [showToast]);

  const handleToastClose = useCallback(() => {
    setToastOpen(false);
  }, []);

  return (
    <>
      <Eventcalendar
        clickToCreate={true}
        dragToCreate={true}
        dragToMove={true}
        dragToResize={true}
        eventDelete={true}
        cssClass="mds-healthcare"
        data={myEvents}
        dragTimeStep={20}
        eventOverlap={false}
        groupBy="date"
        invalid={myInvalids}
        resources={myResources}
        view={myView}
        renderSchedulerEventContent={customScheduleEventContent}
        renderResource={customResource}
        onEventCreated={handleEventCreated}
        onEventDeleted={handleEventDeleted}
        onEventUpdated={handleEventUpdated}
      />
      <Toast message={toastText} isOpen={isToastOpen} onClose={handleToastClose} />
    </>
  );
};

export default App;
```

### CSS

```css
.mds-healthcare .mbsc-schedule-time-col,
.mds-healthcare .mbsc-schedule-time-wrapper {
  width: 80px;
}

.mds-healthcare .mbsc-schedule-resource-title {
  padding: 0;
}

.mds-healthcare-res-col {
  width: 210px;
  border: 0 !important;
}

/* Availability column */

.mds-healthcare-res-col-bar {
  width: 14px;
}

.mds-healthcare-res-col-bar .mbsc-schedule-event {
  margin-left: 5px;
}

.mds-healthcare-res-col-bar .mbsc-schedule-event-range {
  display: none;
}

.mds-healthcare-res-col-bar .mbsc-schedule-event-bar {
  width: 4px;
  border-radius: 3px;
}

/* Resource header */

.mds-healthcare-res-cont {
  padding: 10px;
  font-weight: 500;
  text-align: start;
}

.mds-healthcare-res-name {
  font-size: 16px;
  font-weight: 600;
  line-height: 20px;
}

.mds-healthcare-res-desc {
  font-size: 12px;
  line-height: 14px;
  margin-top: 4px;
}

.mds-healthcare-res-avatar {
  height: 40px;
  width: 40px;
  margin: 10px 0;
}
```

## Implementation notes

- Use `type: 'week'` with `startDay: 1`, `endDay: 5`, `startTime: '08:00'`, `endTime: '18:00'`, `timeCellStep: 20`, `timeLabelStep: 20`, and `allDay: false`. Set `groupBy: 'date'` so all doctor columns appear side by side under each day heading.
- Define resources in interleaved pairs for each doctor: a narrow "bar" resource (even ID, `cssClass: 'mds-healthcare-res-col-bar'`) immediately followed by the doctor resource (odd ID, `name`, `img`, `description`, `color`, `cssClass: 'mds-healthcare-res-col'`). Bar resources 4, 6, 8, 10, 12 also carry `eventCreation: false`; bar resource 2 does not. The bar column renders as a narrow green availability track to the left of the doctor's appointment column.
- Add a `type: 'availability'` event for each bar resource covering the doctor's working hours (`start`/`end`), with `recurring: { repeat: 'daily' }` and `editable: false`. The bar events serve purely as a visual availability indicator - their content is suppressed via `renderSchedulerEventContent`.
- Define `invalid` entries per doctor pair: apply two recurring `invalid` entries to both the bar and doctor resource IDs - one covering `'00:00'` to the working-hours start and one from the working-hours end to `'24:00'`, each with `recurring: { repeat: 'daily' }`. This greys out off-hours on both the bar and appointment columns simultaneously.
- Use `renderSchedulerEventContent` (Vue: `#schedulerEventContent` slot; Angular: `[schedulerEventContentTemplate]`) to render event content: return a "Patient: name" label for regular appointment events (check `!event.original.type`), and an empty/null value for availability bar events to hide their content entirely.
- Use `renderResource` (Vue: `#resource` slot; Angular: `[resourceTemplate]`) to differentiate the two column types: for doctor columns (`cssClass === 'mds-healthcare-res-col'`), render an avatar image alongside the doctor's name and description; for bar columns, render an empty fragment.
- Set `eventOverlap: false` and `dragTimeStep: 20`. Drag creation, move, and resize are left commented out in the demo.
- In `onEventCreated`, `onEventUpdated`, and `onEventDeleted`, show a `Toast` with a short status message.

## Run it in your project

1. If not signed up yet, or don't own a license, you can [start a free trial](https://mobiscroll.com/starttrial).

2. Clone the repository:

   ```
   git clone https://github.com/acidb/mobiscroll-demos-react.git
   ```

3. If not installed yet, install the Mobiscroll CLI from NPM:

   ```
   npm install -g @mobiscroll/cli
   ```

4. Config Mobiscroll - open a terminal window in the root directory and run:

   ```
   mobiscroll config react
   ```

5. Install dependencies - open a terminal window in the root directory and run:

   ```
   npm install
   ```

6. Run the project:

   ```
   npm run dev
   ```

On a trial, add `--trial` to the config command: `mobiscroll config react --trial`.

Running a demo locally requires a Mobiscroll trial or a license; setup is per framework at https://mobiscroll.com/docs/react/getting-started/installation

## Related demos

- Previous: [Appointment Scheduling](https://demo.mobiscroll.com/react/scheduler/doctors-appointment.md) - Build a React appointment scheduler — assign doctors or dentists, drag from an unscheduled queue, and prevent double-booking.
- Next: [24-Hour Rotating Crew Shift Rota](https://demo.mobiscroll.com/react/scheduler/24-hour-manufacturing-shift-rota-planning.md) - Build a 24-hour rotating crew rota with the React Scheduler — three crews, three shifts, and automatic conflict resolution when shifts move.
