React Gantt Overview
React Gantt is available under Commercial, Enterprise and Ultimate licenses. If you're using Individual or GPL editions of Gantt, please refer to the How to Start article for React.
Overview
DHTMLX Gantt is a pure JS component that can work in any browser environment. The Commercial and higher editions of Gantt include a React Gantt component that encapsulates DHTMLX Gantt and allows you to use it natively with React.
The wrapper lets you create a fully functional Gantt chart in your React applications using the familiar props/state model. Under the hood, it manages a standard DHTMLX Gantt instance, translating your React props (such as tasks and config) into the corresponding Gantt initialization and data structures.
Key features
- Declarative data handling: Pass an array of tasks, links, resources, etc. as props.
- Configurable: Map React props to the underlying gantt.config, gantt.templates, gantt.plugins, etc.
- Access to the full Gantt API: Use a ref to call methods like getTask, updateTask, or addTaskLayer.
- Easy customization: Use React components for templates, lightbox forms, or inline editors.
If you're new to DHTMLX Gantt, see the DHTMLX Gantt documentation for an overview of features like Work Time Calculation, Auto Scheduling, Resource Management, and more.
Installation and NPM Access
For up-to-date installation instructions for both the Evaluation and Professional builds, including npm registry configuration and offline examples, see the Installation Guide.
Once you have installed the package, you can import the wrapper in your React code as follows:
// Evaluation build (public npm)
import ReactGantt from '@dhtmlx/trial-react-gantt';
import '@dhtmlx/trial-react-gantt/dist/react-gantt.css';
// Professional build (private npm)
import ReactGantt from '@dhx/react-gantt';
import '@dhx/react-gantt/dist/react-gantt.css';
Version Requirements
- React
v18.0.0or newer
Basic Usage
Here is a minimal snippet showing how to import and render the Gantt chart:
import { useState } from 'react';
import ReactGantt from '@dhx/react-gantt';
import '@dhx/react-gantt/dist/react-gantt.css';
import { demoData } from './DemoData'
export default function BasicGantt() {
const [theme, setTheme] = useState("terrace");
const [tasks, setTasks] = useState(demoData.tasks);
const [links, setLinks] = useState(demoData.links);
return (
<div style={ { height: '500px' }}>
<ReactGantt
tasks={tasks}
links={links}
theme={theme}
/>
</div>
);
}
Note that the above snippet shows how to include the commercial Gantt version. To use the trial code sources, include the package in the following way:
import ReactGantt from '@dhtmlx/trial-react-gantt';
import '@dhtmlx/trial-react-gantt/dist/react-gantt.css';
Where demoData has the following format:
export const demoData = {
tasks: [
{ id: 1, text: "Product Launch", type: "project", open: true, parent: 0},
{ id: 2, text: "Planning Phase", type: "project", open: true, parent: 1},
{ id: 3, text: "Requirement Gathering", type: "task", progress: 0.2,
start_date: "01-06-2025", duration: 3, parent: 2},
{ id: 4, text: "Technical Feasibility", type: "task", progress: 0.4,
start_date: "04-06-2025", duration: 2, parent: 2},
{ id: 5, text: "Implementation Phase", type: "project", progress: 0.1,
open: true, start_date: "08-06-2025", duration: 10, parent: 1},
{ id: 6, text: "Prototype Development", type: "task", progress: 0.0,
start_date: "08-06-2025", duration: 4, parent: 5},
{ id: 7, text: "Feature Testing", type: "task", progress: 0.0,
start_date: "12-06-2025", duration: 4, parent: 5},
{ id: 8, text: "Go-Live Milestone", type: "milestone", progress: 0,
start_date: "18-06-2025", duration: 0, parent: 1}
],
links: [
{ id: 1, source: 3, target: 4, type: "0" },
{ id: 2, source: 4, target: 5, type: "0" },
{ id: 3, source: 6, target: 7, type: "0" },
{ id: 4, source: 7, target: 8, type: "0" }
]
};
export {demoData};
Related article: dhtmlxReactGantt and Firebase Integration
Binding Data
The React Gantt wrapper offers flexible ways of loading and saving data. Conceptually, there are two primary approaches to manage changes in your Gantt data:
- React (or a state manager) as the source of truth
- Gantt as the source of truth
Either approach is valid, but you should pick one and follow it consistently to avoid unexpected behavior.
This section gives a high-level overview of the two binding models. For a more detailed guide, including full examples, see Basics.
React (or a state manager) as the source of truth
In this pattern, ReactGantt receives all task/link data via props (from useState, Redux, Zustand, etc.). Whenever the user modifies tasks or links in the chart, Gantt calls the data.save callback. In that callback, you update your application state. When the state changes, React re-renders ReactGantt, and the Gantt instance is synchronized with the latest data.
import { useMemo, useState } from 'react';
import ReactGantt, { type Task, type Link } from '@dhtmlx/trial-react-gantt';
import '@dhtmlx/trial-react-gantt/dist/react-gantt.css';
export function MyGanttApp({ initialTasks, initialLinks }: {
initialTasks: Task[];
initialLinks: Link[];
}) {
const [tasks, setTasks] = useState<Task[]>(initialTasks);
const [links, setLinks] = useState<Link[]>(initialLinks);
const data = useMemo(
() => ({
save: (entity: string, action: string, item: any, id: string | number) => {
if (entity === 'task') {
setTasks((prev) => {
if (action === 'create') return [...prev, item as Task];
if (action === 'update') return prev.map((task) =>
task.id === id ? (item as Task) : task
);
if (action === 'delete') return prev.filter((task) => task.id !== id);
return prev;
});
}
if (entity === 'link') {
setLinks((prev) => {
if (action === 'create') return [...prev, item as Link];
if (action === 'update') return prev.map((link) =>
link.id === id ? (item as Link) : link
);
if (action === 'delete') return prev.filter((link) => link.id !== id);
return prev;
});
}
},
}),
[]
);
return (
<ReactGantt
tasks={tasks}
links={links}
data={data}
/>
);
}
This approach makes your React (or global) state the single source of truth. It works naturally with state managers such as Redux Toolkit, Zustand, MobX, Jotai, XState, or Valtio - you simply replace useState with your store hooks/selectors and move the update logic into the store.
For more examples (including integrations with specific managers) see React state as the source of truth.
Gantt as the source of truth
In this approach, Gantt itself keeps the authoritative copy of the data. You still initialize or load tasks and links (via props or URLs), but once the chart is running, Gantt handles changes internally and forwards updates to your backend or a custom handler, without going through React state on every edit.
import ReactGantt from '@dhtmlx/trial-react-gantt';
import '@dhtmlx/trial-react-gantt/dist/react-gantt.css';
export function GanttTransportExample() {
return (
<ReactGantt
data={{
load: '/api/gantt/data', // Gantt loads tasks/links from this endpoint
save: '/api/gantt/data', // Gantt sends changes back here
}}
/>
);
}
In this mode:
- the local Gantt instance remains the primary holder of the current data
- React doesn't re-render on every task/link change
- bulk operations like Auto Scheduling are cheaper, because they don't trigger repeated React updates.
If you still keep some representation of tasks/links in React state, be careful not to overwrite Gantt's internal state with stale data.
For more details see Gantt as the source of truth.