Sitemap

Simplifying State Management in DHTMLX React Gantt and Scheduler-Powered Apps with Zustand

8 min readFeb 27, 2026

--

Press enter or click to view image in full size

Not long ago, we explored how state management affects complex UI components like DHTMLX React Gantt and how integrating Redux Toolkit can help bring structure to growing React applications. That approach works well, especially when you need strict control over state transitions. But in practice, not every project needs that level of ceremony.

State management in modern front-end apps rarely follows a single pattern. Some teams value predictability and explicit flows. Others prefer something lighter that doesn’t add extra boilerplate. Redux Toolkit remains a solid choice for feature-rich applications, but we often see cases where its setup feels heavier than necessary, especially in projects where speed and simplicity matter just as much as scalability.

So we decided to continue our series on state management strategies for apps built with DHTMLX React Gantt and React Scheduler.

This time, the focus is on Zustand, a minimalistic yet surprisingly capable state management library. We’ll look at how it fits into real React workflows, why many developers reach for it instead of larger state solutions, and what actually happens when you connect a Zustand store to a Gantt chart or scheduling calendar in a working application.

Why Zustand Caught So Much Attention

Zustand is a lightweight state management library built with React in mind. Since its first stable release back in 2019, it has steadily built a strong following, mostly because it stays out of your way. The focus is simple: minimal API surface, solid performance, and a developer experience that doesn’t feel like overhead.

The adoption numbers reflect that momentum. At the time of writing, Zustand is approaching 21 million weekly downloads on npm, and it recently ranked at the top among state management libraries in the latest edition of the JavaScript Rising Stars report.

Source: JavaScript Rising Stars 2025

So what’s behind that growth?

In practice, teams are drawn to its flexibility. Zustand uses a hook-based API that feels natural inside React components. There’s no heavy architecture imposed on you, no rigid folder structure to follow, and very little boilerplate. You define a store, consume it where needed, and move on. State updates remain predictable thanks to immutable patterns, but without the ceremony we often associate with larger Flux-inspired solutions.

Another detail that developers appreciate: Zustand avoids a few common React pitfalls. Issues like the “zombie child” problem, React concurrency edge cases, or context loss between mixed renderers are largely handled under the hood. You don’t spend time fighting the state layer; it just works.

And it’s not strictly tied to React either. Zustand can be used with vanilla JavaScript and even in Node.js environments. Unlike Redux, which is deeply rooted in React-oriented patterns, Zustand gives you more freedom to manage state beyond a single UI framework.

All of that makes it an interesting alternative to Redux Toolkit, especially when you want something leaner without sacrificing control. So how does that difference actually play out in a real project with DHTMLX React Gantt or Scheduler? Let’s take a closer look.

Zustand vs Redux Toolkit: Where the Difference Shows

Zustand and Redux Toolkit appeared in the React ecosystem around the same period. Redux Toolkit gained traction quickly, mostly because it became the officially recommended way to write Redux logic. For teams already invested in Redux, switching to Redux Toolkit felt natural — almost automatic.

But things have been shifting.

According to the latest State of React 2025 survey, Zustand is gaining ground fast. In fact, respondents rated it higher than Redux Toolkit across categories like Interest, Positivity, and Satisfaction. That says a lot about where developer sentiment is moving.

Press enter or click to view image in full size
Source: State of React 2025

In practice, the reason isn’t hard to see. When you’re building feature-heavy interfaces, like scheduling systems or project planning apps with Gantt charts, state logic can grow quickly. Many teams don’t necessarily want more structure at that point. They want clarity without ceremony. Zustand tends to hit that balance.

One notable difference is architectural: Zustand doesn’t require wrapping your entire app in a context provider. You define a store, consume it with hooks, and keep going. For some projects, that small shift already simplifies the mental model.

Zustand is not a universal replacement for Redux Toolkit. If you’re dealing with strict middleware chains, complex async workflows, or deeply interconnected global state, Redux Toolkit remains a strong fit.

But for many React scheduling tools and project planning systems, especially those built with DHTMLX React Gantt or React Scheduler, Zustand provides enough control over edits and updates without adding structural weight. Teams usually notice that the store stays easy to reason about even as the UI becomes more interactive.

With that in mind, let’s look at how this plays out when integrating Zustand with DHTMLX React components.

Both DHTMLX React Gantt and React Scheduler follow a similar integration pattern. To keep things concrete, we’ll focus on DHTMLX React Gantt as an example.

If you’d like to try it yourself, you can install the professional evaluation version directly from npm and experiment in a real project setup.

Key Aspects of Integrating DHTMLX React Gantt with Zustand

At this point, it’s fair to ask: how do all those advantages of Zustand actually translate into a real React Gantt project?

Instead of speaking in abstractions, let’s look at the practical side. The goal here is to build a state layer around DHTMLX React Gantt where every update, whether it’s editing a task, changing zoom, or creating a dependency, flows through a centralized Zustand store.

Defining Store Responsibilities

The store plays a bigger role than just holding Gantt data. It also defines how that data changes.

Here’s the TypeScript structure used in the example:

type Snapshot = { tasks: SerializedTask[]; links: Link[]; config: GanttConfig };
type State = {
tasks: SerializedTask[];
links: Link[];
config: GanttConfig;
past: Snapshot[];
future: Snapshot[];
maxHistory: number;
recordHistory: () => void;
undo: () => void;
redo: () => void;

setZoom: (level: ZoomLevel) => void;
addTask: (task: SerializedTask) => SerializedTask;
upsertTask: (task: SerializedTask) => void;
deleteTask: (id: string | number) => void;
addLink: (l: Link) => Link;
upsertLink: (l: Link) => void;
deleteLink: (id: string | number) => void;
};

What stands out here is that the store combines data, history management, and mutation logic in one place. Tasks, links, configuration, undo/redo — everything lives together. In practice, this makes the store the single source of truth for the entire chart.

Creating the Store

With the structure defined, we can create the Zustand store:

export const useGanttStore = create<State>((set, get) => ({
tasks: seedTasks,
links: seedLinks,
config: { zoom: defaultZoomLevels },

past: [],
future: [],
maxHistory: 50,
... // actions will go here

At this stage, we have an initial state: tasks, links, config, and empty history arrays. The interesting part is how actions modify that state.

Take Zoom changes as an example:

setZoom: (level) => {
get().recordHistory();
set({
config: { ...get().config, zoom: { ...get().config.zoom, current: level } },
});
},

Before updating anything, the store records the current snapshot. Then it creates a new configuration object with the updated zoom level. No reducers. No dispatch. Just direct, explicit state transitions.

Updating a task follows the same pattern:

upsertTask: (task) => {
get().recordHistory();
const tasks = get().tasks;
const index = tasks.findIndex((x) => String(x.id) === String(task.id));
if (index !== -1) {
set({
tasks: [...tasks.slice(0, index), { ...tasks[index], ...task }, ...tasks.slice(index + 1)],
});
}
},

The logic is straightforward: find the task, merge changes, update state. Unlike Redux Toolkit, there’s no separate reducer layer. Data and mutation logic live inside the same store definition. For many teams, that reduces mental overhead.

The same principle applies to links, configuration updates, and undo/redo history. Everything is handled through regular store actions.

Which leads to the next practical question: how do user interactions inside the Gantt chart trigger these actions?

Turning Gantt Events into Store Actions

This is where the data.save callback comes in. It acts as a bridge between DHTMLX React Gantt and the Zustand store.

Whenever a user edits a task or link, Gantt triggers data.save. That callback determines what kind of operation occurred and routes it to the appropriate store action.

const data: ReactGanttProps['data'] = useMemo(  
() => ({
save: (entity, action, item, id) => {
if (entity === 'task') {
const task = item as SerializedTask;
if (action === 'create') return addTask(task);
else if (action === 'update') upsertTask(task);
else if (action === 'delete') deleteTask(id);
} else if (entity === 'link') {
const link = item as Link;
if (action === 'create') return addLink(link);
else if (action === 'update') upsertLink(link);
else if (action === 'delete') deleteLink(id);
}
},
}),
[addTask, addLink, upsertTask, upsertLink, deleteTask, deleteLink]
);

In practice, the flow looks like this:

  • A user edits a task or dependency in the Gantt UI
  • Gantt calls data.save(…)
  • The action type is identified
  • The corresponding Zustand action runs
  • The store updates
  • The chart re-renders with the new state

All mutations happen inside the store. The Gantt component simply reflects the updated data. Because every change goes through explicit store actions, debugging and change tracking become more predictable.

If you want to explore this mechanism in more detail, the “Handling changes with data.save” section in the official guide walks through additional examples.

Keeping Gantt as a Visual Layer

One deliberate design choice in this approach is the separation of concerns. Once Zustand handles state management, the Gantt component itself becomes mostly a rendering layer.

Here’s how the store is consumed:

const { tasks, links, config, setZoom, addTask, upsertTask, deleteTask, addLink, upsertLink, deleteLink, undo, redo } = useGanttStore();

And rendering becomes straightforward:

<ReactGantt ref={ganttRef} tasks={tasks} links={links} config={config} templates={templates} data={data} />

The component reads the state and passes events back to the store. That’s it. No duplicated logic. No hidden mutations inside the UI layer.

If you prefer a full walkthrough, the official documentation includes a step-by-step tutorial showing how to connect DHTMLX React Gantt with Zustand in a single application. It also demonstrates how to build a custom toolbar for zoom controls and undo/redo actions outside the chart UI, all wired into the same store. There’s even a complete working example available on GitHub for those who want to experiment in a real project setup.

Wrapping Up

When developing a React application that includes complex UI components such as a Gantt chart or Scheduler, state management quickly becomes a central architectural concern. The combination of React, Zustand, and DHTMLX React Gantt demonstrates that powerful functionality does not necessarily require heavyweight solutions.

Zustand offers a clean, minimalistic approach to handling tasks, links, configuration settings, and even undo/redo history — all within a single, predictable store. The result is a setup where data flows are transparent, debugging is simpler, and the Gantt component remains focused purely on rendering.

Is Zustand a solid choice for enterprise-grade React applications? In many cases, absolutely. Still, architecture decisions should always align with project requirements. In upcoming articles, we will evaluate other state management approaches for DHTMLX React components to help you make an informed decision.

--

--

JavaScript UI Libraries — DHTMLX
JavaScript UI Libraries — DHTMLX

Written by JavaScript UI Libraries — DHTMLX

Here we post news about our JavaScript UI libraries. In addition to this, we also share useful tips, news and articles about JavaScript.