Data Binding & State Management in React Gantt
React Gantt supports two data binding patterns:
- React state as the source of truth - recommended for most React apps.
- Gantt as the source of truth - useful for specialized cases.
Both approaches are valid, but you should pick one and follow it consistently to avoid unexpected behavior.
This article explains both modes and shows basic examples of each.
If you haven't rendered a basic chart yet, start from the Quick Start.
Data Models
React state as the source of truth (recommended)
In this model:
- you keep
tasks,links,resources,resourceAssignmentsin React state or a state library - you pass them to
<Gantt>as props - when a user changes something, ReactGantt calls your
data.saveordata.batchSavecallback - you update React state -> React re-renders -> ReactGantt re-reads the new props.
This is the right choice in case your page has other React UI that must see the same data as Gantt and when your application has additional React components or uses a state manager that relies on the same data.
However, it will require more frequent re-parsing or re-rendering of the Gantt.
Gantt as the source of truth
In this approach, you treat ReactGantt and your backend as the main owner of the data:
- ReactGantt loads the initial dataset via
data.load, or via props, or via an imperative API call - ReactGantt applies user changes internally and/or sends them to the server
- you do not keep a mirrored copy of all tasks/links in React state that is constantly fed back into props.
The key difference is the absence of a full loop - user changes do not update the React state and React does not re-apply updated props after each change.
This model is useful when datasets are very large as it reduces the overhead of constantly updating React state when Gantt data changes and simplifies large-batch operations (like auto-scheduling) without repeated re-renders.
On the other hand, you lose the direct synchronization between Gantt data and your React state. And if you do store tasks/links in a React state, you need to be sure not to unintentionally overwrite Gantt's internal state.
React state as the source of truth
In this pattern, you hold all core collections in state and pass them as props (tasks, links, resources, resourceAssignments). Whenever the user modifies tasks or links inside the Gantt (for example, by creating or deleting a task), the Gantt triggers a callback. In this callback, you update your React state with the new or removed data. Once the state is updated, React re-renders the ReactGantt component, which in turn reads the updated props from the latest state.
Minimal example with React state
import { useState } from 'react';
import Gantt, {
Task,
Link
} from "@dhtmlx/trial-react-gantt";
import "@dhtmlx/trial-react-gantt/dist/react-gantt.css";
import { demoData } from "./demoData";
export default function ReactStateGantt() {
const [tasks, setTasks] = useState<Task[]>(demoData.tasks);
const [links, setLinks] = useState<Link[]>(demoData.links);
return (
<div style={{ height: "100vh" }}>
<Gantt
tasks={tasks}
links={links}
data={{
save: (entity, action, item, id) => {
// Update React state here (see below for patterns)
console.log("Change:", { entity, action, id, item });
},
}}
/>
</div>
);
}
This gives you a basic starting point - React controls what is rendered via props, Gantt reports changes via save callback and makes React the authoritative owner of the data.
The next sections show the typical patterns you implement inside that callback.