DevExtreme v24.2 is now available.

Explore our newest features/capabilities and share your thoughts with us.

Your search did not match any results.

React Scheduler - Overview

DevExtreme React Scheduler is a UI component for scheduling. It implements all the features necessary for its purpose: flexible data binding, easy appointment editing, multiple calendar views, time zones support, and more. Learn more about DevExtreme React components.

To get started with the DevExtreme Scheduler component, refer to the following tutorial for step-by-step instructions: Getting Started with Scheduler.

DevExtreme Accessibility Compliance
DevExtreme component libraries meet a variety of WCAG and Section 508 compliance standards. To assess this demo’s accessibility level, click the Run AXE® Validation button to launch the AXE® web accessibility evaluation tool.
All trademarks or registered trademarks are property of their respective owners. AXE® Terms of Use
The overall accessibility level of your application depends on the Scheduler features used.
Backend API
import React from 'react'; import Scheduler, { Resource, SchedulerTypes } from 'devextreme-react/scheduler'; import { employees, data } from './data.ts'; import DataCell from './DataCell.tsx'; import ResourceCell from './ResourceCell.tsx'; const currentDate = new Date(2021, 5, 2, 11, 30); const groups = ['employeeID']; const views: SchedulerTypes.ViewType[] = ['month']; const App = () => ( <Scheduler timeZone="America/Los_Angeles" dataSource={data} dataCellComponent={DataCell} resourceCellComponent={ResourceCell} groups={groups} views={views} defaultCurrentView="month" defaultCurrentDate={currentDate} height={710} showAllDayPanel={true} firstDayOfWeek={1} startDayHour={8} endDayHour={18} > <Resource label="Employee" fieldExpr="employeeID" dataSource={employees} allowMultiple={false} /> </Scheduler> ); export default App;
import React from 'react'; import Scheduler, { Resource } from 'devextreme-react/scheduler'; import { employees, data } from './data.js'; import DataCell from './DataCell.js'; import ResourceCell from './ResourceCell.js'; const currentDate = new Date(2021, 5, 2, 11, 30); const groups = ['employeeID']; const views = ['month']; const App = () => ( <Scheduler timeZone="America/Los_Angeles" dataSource={data} dataCellComponent={DataCell} resourceCellComponent={ResourceCell} groups={groups} views={views} defaultCurrentView="month" defaultCurrentDate={currentDate} height={710} showAllDayPanel={true} firstDayOfWeek={1} startDayHour={8} endDayHour={18} > <Resource label="Employee" fieldExpr="employeeID" dataSource={employees} allowMultiple={false} /> </Scheduler> ); export default App;
import React from 'react'; type DataCellProps = { data: { startDate: Date; groups: { employeeID: number; }; text: string; } }; const isWeekEnd = (date: Date) => { const day = date.getDay(); return day === 0 || day === 6; }; const getCurrentTraining = (date: number, employeeID: number) => { const result = (date + employeeID) % 3; const currentTraining = `training-background-${result}`; return currentTraining; }; const DataCell = (props: DataCellProps) => { const { data: { startDate, groups: { employeeID }, text } } = props; const dayClasses = [ 'day-cell', getCurrentTraining(startDate.getDate(), employeeID), ]; const employeeClasses = [`employee-${employeeID}`, 'dx-template-wrapper']; if (isWeekEnd(startDate)) { employeeClasses.push(`employee-weekend-${employeeID}`); } return ( <div className={employeeClasses.join(' ')}> <div className={dayClasses.join(' ')}> {text} </div> </div> ); }; export default DataCell;
import React from 'react'; type ResourceCellProps = { data: { color: string; text: string; data: { avatar: string; age: number; discipline: string; }; }; }; const ResourceCell = (props: ResourceCellProps) => { const { data: { color, text, data: { avatar, age, discipline } } } = props; return ( <div className="dx-template-wrapper"> <div className="name" style={{ background: color }}> <h2>{text}</h2> </div> <div className="avatar" title={text}> <img src={avatar} alt={`${text} photo`} /> </div> <div className="info" style={{ color }}> Age: {age} <br /> <b>{discipline}</b> </div> </div> ); }; export default ResourceCell;
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.tsx'; ReactDOM.render( <App />, document.getElementById('app'), );
import { SchedulerTypes } from 'devextreme-react/scheduler'; type Appointment = SchedulerTypes.Appointment & { employeeID: number }; type Resource = { text: string; id: number; color: string; avatar: string; age: number; discipline: string; }; export const data: Appointment[] = [ { text: 'Helen', employeeID: 2, startDate: new Date('2021-06-01T16:30:00.000Z'), endDate: new Date('2021-06-01T18:30:00.000Z'), }, { text: 'Helen', employeeID: 2, startDate: new Date('2021-06-10T16:30:00.000Z'), endDate: new Date('2021-06-11T18:30:00.000Z'), }, { text: 'Alex', employeeID: 1, startDate: new Date('2021-06-02T16:30:00.000Z'), endDate: new Date('2021-06-02T18:30:00.000Z'), }, { text: 'Alex', employeeID: 1, startDate: new Date('2021-06-11T19:00:00.000Z'), endDate: new Date('2021-06-11T20:00:00.000Z'), }, { text: 'Alex', employeeID: 2, startDate: new Date('2021-06-16T16:30:00.000Z'), endDate: new Date('2021-06-16T18:30:00.000Z'), }, { text: 'Stan', employeeID: 1, startDate: new Date('2021-06-07T16:30:00.000Z'), endDate: new Date('2021-06-07T18:30:00.000Z'), }, { text: 'Stan', employeeID: 1, startDate: new Date('2021-06-28T16:30:00.000Z'), endDate: new Date('2021-06-28T18:30:00.000Z'), }, { text: 'Stan', employeeID: 1, startDate: new Date('2021-06-30T16:30:00.000Z'), endDate: new Date('2021-06-30T18:30:00.000Z'), }, { text: 'Rachel', employeeID: 2, startDate: new Date('2021-06-04T16:30:00.000Z'), endDate: new Date('2021-06-04T18:30:00.000Z'), }, { text: 'Rachel', employeeID: 2, startDate: new Date('2021-06-07T16:30:00.000Z'), endDate: new Date('2021-06-07T18:30:00.000Z'), }, { text: 'Rachel', employeeID: 1, startDate: new Date('2021-06-21T16:30:00.000Z'), endDate: new Date('2021-06-21T18:30:00.000Z'), }, { text: 'Kelly', employeeID: 2, startDate: new Date('2021-06-15T16:30:00.000Z'), endDate: new Date('2021-06-15T18:30:00.000Z'), }, { text: 'Kelly', employeeID: 2, startDate: new Date('2021-06-29T16:30:00.000Z'), endDate: new Date('2021-06-29T18:30:00.000Z'), }]; export const employees: Resource[] = [{ text: 'John Heart', id: 1, color: 'var(--background-color-1)', avatar: '../../../../images/employees/19.png', age: 27, discipline: 'ABS, Fitball, StepFit', }, { text: 'Greta Sims', id: 2, color: 'var(--background-color-2)', avatar: '../../../../images/employees/31.png', age: 25, discipline: 'ABS, Fitball, StepFit', }];
window.exports = window.exports || {}; window.config = { transpiler: 'ts', typescriptOptions: { module: 'system', emitDecoratorMetadata: true, experimentalDecorators: true, jsx: 'react', }, meta: { 'react': { 'esModule': true, }, 'typescript': { 'exports': 'ts', }, 'devextreme/time_zone_utils.js': { 'esModule': true, }, 'devextreme/localization.js': { 'esModule': true, }, 'devextreme/viz/palette.js': { 'esModule': true, }, 'openai': { 'esModule': true, }, }, paths: { 'npm:': 'https://unpkg.com/', 'bundles:': '../../../../bundles/', 'externals:': '../../../../bundles/externals/', }, defaultExtension: 'js', map: { 'ts': 'npm:plugin-typescript@8.0.0/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.js', 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js', 'react': 'npm:react@17.0.2/umd/react.development.js', 'react-dom': 'npm:react-dom@17.0.2/umd/react-dom.development.js', 'prop-types': 'npm:prop-types/prop-types.js', 'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js', 'luxon': 'npm:luxon@3.4.4/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign', 'devextreme': 'npm:devextreme@link:../../packages/devextreme/artifacts/npm/devextreme/cjs', 'devextreme-react': 'npm:devextreme-react@link:../../packages/devextreme-react/npm/cjs', 'devextreme-quill': 'npm:devextreme-quill@1.7.1/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.5/dist/dx-diagram.js', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.54/dist/dx-gantt.js', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.12', 'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js', 'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js', 'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js', 'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js', 'inferno-hydrate': 'npm:inferno-hydrate/dist/inferno-hydrate.min.js', 'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js', 'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js', 'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js', 'devextreme-cldr-data': 'npm:devextreme-cldr-data@1.0.3', // SystemJS plugins 'plugin-babel': 'npm:systemjs-plugin-babel@0.0.25/plugin-babel.js', 'systemjs-babel-build': 'npm:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js', // Prettier 'prettier/standalone': 'npm:prettier@2.8.8/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.8/parser-html.js', }, packages: { 'devextreme': { defaultExtension: 'js', }, 'devextreme-react': { main: 'index.js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/localization/messages': { format: 'json', defaultExtension: 'json', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.12/inferno/package.json', ], babelOptions: { sourceMaps: false, stage0: true, react: true, }, }; System.config(window.config);
import React from 'react'; const isWeekEnd = (date) => { const day = date.getDay(); return day === 0 || day === 6; }; const getCurrentTraining = (date, employeeID) => { const result = (date + employeeID) % 3; const currentTraining = `training-background-${result}`; return currentTraining; }; const DataCell = (props) => { const { data: { startDate, groups: { employeeID }, text, }, } = props; const dayClasses = ['day-cell', getCurrentTraining(startDate.getDate(), employeeID)]; const employeeClasses = [`employee-${employeeID}`, 'dx-template-wrapper']; if (isWeekEnd(startDate)) { employeeClasses.push(`employee-weekend-${employeeID}`); } return ( <div className={employeeClasses.join(' ')}> <div className={dayClasses.join(' ')}>{text}</div> </div> ); }; export default DataCell;
import React from 'react'; const ResourceCell = (props) => { const { data: { color, text, data: { avatar, age, discipline }, }, } = props; return ( <div className="dx-template-wrapper"> <div className="name" style={{ background: color }} > <h2>{text}</h2> </div> <div className="avatar" title={text} > <img src={avatar} alt={`${text} photo`} /> </div> <div className="info" style={{ color }} > Age: {age} <br /> <b>{discipline}</b> </div> </div> ); }; export default ResourceCell;
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.js'; ReactDOM.render(<App />, document.getElementById('app'));
export const data = [ { text: 'Helen', employeeID: 2, startDate: new Date('2021-06-01T16:30:00.000Z'), endDate: new Date('2021-06-01T18:30:00.000Z'), }, { text: 'Helen', employeeID: 2, startDate: new Date('2021-06-10T16:30:00.000Z'), endDate: new Date('2021-06-11T18:30:00.000Z'), }, { text: 'Alex', employeeID: 1, startDate: new Date('2021-06-02T16:30:00.000Z'), endDate: new Date('2021-06-02T18:30:00.000Z'), }, { text: 'Alex', employeeID: 1, startDate: new Date('2021-06-11T19:00:00.000Z'), endDate: new Date('2021-06-11T20:00:00.000Z'), }, { text: 'Alex', employeeID: 2, startDate: new Date('2021-06-16T16:30:00.000Z'), endDate: new Date('2021-06-16T18:30:00.000Z'), }, { text: 'Stan', employeeID: 1, startDate: new Date('2021-06-07T16:30:00.000Z'), endDate: new Date('2021-06-07T18:30:00.000Z'), }, { text: 'Stan', employeeID: 1, startDate: new Date('2021-06-28T16:30:00.000Z'), endDate: new Date('2021-06-28T18:30:00.000Z'), }, { text: 'Stan', employeeID: 1, startDate: new Date('2021-06-30T16:30:00.000Z'), endDate: new Date('2021-06-30T18:30:00.000Z'), }, { text: 'Rachel', employeeID: 2, startDate: new Date('2021-06-04T16:30:00.000Z'), endDate: new Date('2021-06-04T18:30:00.000Z'), }, { text: 'Rachel', employeeID: 2, startDate: new Date('2021-06-07T16:30:00.000Z'), endDate: new Date('2021-06-07T18:30:00.000Z'), }, { text: 'Rachel', employeeID: 1, startDate: new Date('2021-06-21T16:30:00.000Z'), endDate: new Date('2021-06-21T18:30:00.000Z'), }, { text: 'Kelly', employeeID: 2, startDate: new Date('2021-06-15T16:30:00.000Z'), endDate: new Date('2021-06-15T18:30:00.000Z'), }, { text: 'Kelly', employeeID: 2, startDate: new Date('2021-06-29T16:30:00.000Z'), endDate: new Date('2021-06-29T18:30:00.000Z'), }, ]; export const employees = [ { text: 'John Heart', id: 1, color: 'var(--background-color-1)', avatar: '../../../../images/employees/19.png', age: 27, discipline: 'ABS, Fitball, StepFit', }, { text: 'Greta Sims', id: 2, color: 'var(--background-color-2)', avatar: '../../../../images/employees/31.png', age: 25, discipline: 'ABS, Fitball, StepFit', }, ];
<!DOCTYPE html> <html lang="en"> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/24.2.3/css/dx.light.css" /> <link rel="stylesheet" type="text/css" href="styles.css" /> <script src="https://unpkg.com/core-js@2.6.12/client/shim.min.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script type="text/javascript" src="config.js"></script> <script type="text/javascript"> System.import("./index.tsx"); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <div id="app"></div> </div> </body> </html>
.dx-scheduler-group-header, .dx-scheduler-date-table-cell { position: relative; } .dx-scheduler-group-header-content { padding-left: 8px; } .dx-color-scheme-light, .dx-color-scheme-carmine, .dx-color-scheme-softblue, .dx-color-scheme-blue-light, .dx-color-scheme-saas-light, .dx-color-scheme-lime-light, .dx-color-scheme-orange-light, .dx-color-scheme-purple-light, .dx-color-scheme-teal-light { --text-color-1: rgba(0, 0, 0, .6); --text-color-2: rgba(255, 255, 255, 1); --disabled-color: rgba(0, 0, 0, 0.38); --background-color-1: rgba(50, 134, 56, 1); --background-color-2: rgba(194, 81, 0, 1); } .dx-color-scheme-dark, .dx-color-scheme-darkviolet, .dx-color-scheme-darkmoon, .dx-color-scheme-blue-dark, .dx-color-scheme-saas-dark, .dx-color-scheme-lime-dark, .dx-color-scheme-orange-dark, .dx-color-scheme-purple-dark, .dx-color-scheme-teal-dark { --text-color-1: rgba(255, 255, 255, 1); --text-color-2: rgba(54, 54, 64, 1); --disabled-color: rgba(255, 255, 255, 0.38); --background-color-1: rgba(159, 213, 161, 1); --background-color-2: rgba(255, 181, 127, 1); } .dx-scheduler-header .dx-toolbar .dx-button, .dx-scheduler-header .dx-toolbar .dx-button .dx-icon { color: var(--text-color-1); } .dx-scheduler-date-table-other-month.dx-scheduler-date-table-cell { opacity: 1; color: var(--disabled-color) !important; } .dx-scheduler-work-space-month .dx-scheduler-date-table-cell { color: var(--text-color-1); } .dx-scheduler-work-space-month .dx-scheduler-appointment, .dx-scheduler-work-space-month .dx-scheduler-appointment.dx-state-focused { color: var(--text-color-2); line-height: 22px; } .dx-scheduler-work-space-month .dx-scheduler-appointment .dx-scheduler-appointment-content { padding-top: 0; } .dx-scheduler-date-table-cell .dx-template-wrapper { position: absolute; height: 100%; width: 100%; padding-right: 6px; } .avatar { width: 124px; float: left; overflow: hidden; position: relative; height: 124px; border: 1px solid rgba(0, 0, 0, 0.24); border-radius: 50%; background-color: rgba(255, 255, 255, 1); } .avatar img { position: relative; width: 126px; height: 130px; object-fit: contain; } .avatar[title="John Heart"] img { top: 5px; left: 3px; } .avatar[title="Greta Sims"] img { top: 5px; left: -7px; } .name { position: absolute; bottom: 0; left: 0; width: 100%; } .name h2 { color: var(--text-color-2); font-size: 28px; text-align: left; padding: 0 0 0 170px; margin: 0; height: 40px; line-height: 40px; } .info { width: auto; text-align: left; height: 100%; font-size: 14px; line-height: 20px; font-weight: normal; padding: 25px 20px 25px 40px; color: #707070; } .dx-color-scheme-contrast .info { color: #fff; } .day-cell { width: 100%; height: 100%; background-position: center center; background-repeat: no-repeat; } .dx-scheduler-appointment { color: rgba(255, 255, 255, 1); } .employee-1 { background-color: rgba(55, 126, 58, 0.08); } .employee-2 { background-color: rgba(194, 81, 0, 0.08); } .employee-weekend-1 { background-color: rgba(55, 126, 58, 0.12); } .employee-weekend-2 { background-color: rgba(194, 81, 0, 0.12); } .training-background-0 { background-image: url("../../../../images/Scheduler/Overview/icon-abs.png"); } .training-background-1 { background-image: url("../../../../images/Scheduler/Overview/icon-step.png"); } .training-background-2 { background-image: url("../../../../images/Scheduler/Overview/icon-fitball.png"); }