{"id":15475,"date":"2026-04-30T14:28:13","date_gmt":"2026-04-30T11:28:13","guid":{"rendered":"https:\/\/dhtmlx.com\/blog\/?p=15475"},"modified":"2026-10-26T19:42:05","modified_gmt":"2026-10-26T16:42:05","slug":"enhancing-state-management-dhtmlx-react-gantt-jotai","status":"publish","type":"post","link":"https:\/\/dhtmlx.com\/blog\/enhancing-state-management-dhtmlx-react-gantt-jotai\/","title":{"rendered":"Enhancing State Management in DHTMLX React Gantt with Jotai"},"content":{"rendered":"<p>It is always useful to have more than one way to approach challenging tasks in web development, particularly state management in React apps. It is especially true for projects that rely on project management tools like a Gantt chart. Previously, we discussed how to manage state in apps using DHTMLX React Gantt with predictable <a href=\"https:\/\/dhtmlx.com\/blog\/using-dhtmlx-gantt-chart-for-react-with-redux-toolkit-principles-benefits-and-implementation-tips\/\" rel=\"noopener\" target=\"_blank\">Redux Toolkit<\/a>, lightweight <a href=\"https:\/\/dhtmlx.com\/blog\/using-zustand-state-management-apps-dhtmlx-react-gantt-scheduler\/\" rel=\"noopener\" target=\"_blank\">Zustand<\/a>, reactive <a href=\"https:\/\/dhtmlx.com\/blog\/managing-state-dhtmlx-react-gantt-chart-scheduler-mobx\/\" rel=\"noopener\" target=\"_blank\">Mobx<\/a>, and explicit <a href=\"https:\/\/dhtmlx.com\/blog\/using-xstate-in-react-gantt-and-scheduler-apps-for-complex-state-scenarios\/\" rel=\"noopener\" target=\"_blank\">XState<\/a>. <\/p>\n<p>This time, we\u2019ll explore Jotai and how its bottom-up approach with the atomic model fits into a highly interactive Gantt environment and React&#8217;s hooks-based workflow. As a reference point, we\u2019ll use the <a href=\"https:\/\/github.com\/DHTMLX\/react-gantt-jotai-starter\" rel=\"noopener\" target=\"_blank\">react-gantt-jotai-starter<\/a> demo, which demonstrates how to use Jotai with <a href=\"https:\/\/dhtmlx.com\/docs\/products\/dhtmlxGantt-for-React\/\" rel=\"noopener\" target=\"_blank\">DHTMLX React Gantt<\/a>. <\/p>\n<p>Before delving into technical details, let us first take a quick look at how Jotai works and where it makes the most sense to use it.<\/p>\n<h2>Jotai State Management in React Apps<\/h2>\n<p>Jotai entered the React landscape in 2020 to help development teams deal with extra re-render issues with React context (useContext + useState). During that relatively short period, Jotai gained the trust of many innovative companies like Meta, TikTok, and Adobe. What makes it stand out among other state management options for React?<\/p>\n<p>Jotai is indeed inspired by the atomic state model of Recoil, but is frequently compared to Zustand because both share a philosophy of minimalism, ease of use, and lack of heavy boilerplate. If you take a look at the latest editions of popular surveys (<a href=\"https:\/\/2025.stateofreact.com\/en-US\/libraries\/state-management\/\" rel=\"noopener\" target=\"_blank\">State of React<\/a>, <a href=\"https:\/\/risingstars.js.org\/2025\/en#section-statemanagement\" rel=\"noopener\" target=\"_blank\">JS Rising Stars<\/a>), you can notice that the development community highly appreciates both libraries and puts them close to each other in various rankings. Even the names of both libraries are translated as \u201cstate\u201d (Jotai from Japanese and Zustand from German).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dhtmlx.com\/blog\/wp-content\/uploads\/2026\/04\/image1.gif\" alt=\"Comparing Jotai with Zustand\" width=\"1572\" height=\"628\" class=\"alignleft size-full wp-image-15477\" \/><br \/>\n<em>Source: State of React 2025<\/em><\/p>\n<p>The difference between Jotai and Zustand becomes apparent when you examine their mental models. Zustand offers a centralized store that can be split into modular slices. Jotai takes a different route by breaking down the state into small, independent atoms that do not rely on any central structure. Thus, instead of selecting a piece of state from a store and mutating it through an action, Jotai allows you to work with individual atoms directly. <\/p>\n<p>React components subscribe only to the atoms they use, so updates remain granular and don\u2019t trigger excessive re-renders elsewhere in the UI. This approach helps eliminate the need for memoization and makes state updates more straightforward, without deviating from React\u2019s declarative style. Jotai\u2019s basic API is similar to useState. Still, atoms can also be combined, derived, and used to handle updates (write-only atoms), enabling you to build more complex state models when needed. Jotai is also backed by a solid package of utilities and extensions to help you out. As a result, Jotai works well in the following scenarios: <\/p>\n<ul>\n<li>as a viable alternative to useState+useContext,<\/li>\n<li>you need something as developer-friendly as Zustand,<\/li>\n<li>code splitting is a high priority,<\/li>\n<li>projects that require support for modern React features like Suspense and Transition,<\/li>\n<li>and even use Jotai atoms for complex UI state management in enterprise apps.<\/li>\n<\/ul>\n<p>This flexibility comes with a trade-off. As the number of atoms increases, the overall structure can become harder to control than a single centralized store. At that point, clear naming and organization become crucial.<\/p>\n<h2>Why It is Worth Trying DHTMLX React Gantt with Jotai<\/h2>\n<p>A React Gantt chart built with DHTMLX is commonly a data-intensive tool, where a single user action in the UI, such as editing a task or adding a dependency link, can trigger a range of changes across interdependent tasks and the overall project timeline. Handling this kind of interaction requires a state model that can deal with frequent updates without overcomplicating things. This is where Jotai feels like a reasonable choice. <\/p>\n<p>When state changes are handled through focused atoms, updates become easier to isolate. If you reschedule a task, only the pieces related to this task are affected. The rest of the UI remains untouched. In Gantt charts, where changes happen all the time, this makes it much easier to follow the state changes. You don\u2019t have to guess what else might have changed somewhere else. <\/p>\n<p>To better understand the work of Jotai atoms in the React Gantt demo, first, it is worth paying attention to how the state is structured and how updates flow from the UI into the store.<\/p>\n<h2>Separation of Concerns and Data Flow<\/h2>\n<p>All the Gantt data is placed in the <a href=\"https:\/\/github.com\/DHTMLX\/react-gantt-jotai-starter\/blob\/main\/src\/store.ts\" rel=\"noopener\" target=\"_blank\">Jotai store<\/a>. The current state is kept inside <strong>ganttStateAtom<\/strong>. It is treated as the single source of truth. Updates are handled by write-only atoms, each responsible for a specific type of change, whether it\u2019s editing a task, managing links, or adjusting zoom. <\/p>\n<p>The <a href=\"https:\/\/github.com\/DHTMLX\/react-gantt-jotai-starter\/blob\/main\/src\/components\/GanttComponent.tsx\" rel=\"noopener\" target=\"_blank\">component<\/a> that renders the Gantt mostly just reads this state and passes it down as props. When something happens in the chart, it doesn\u2019t try to process it on its own. Instead, the event is forwarded via data.save, which determines which atom should handle it. <\/p>\n<p>The demo also includes a custom <a href=\"https:\/\/github.com\/DHTMLX\/react-gantt-jotai-starter\/blob\/main\/src\/components\/Toolbar.tsx\" rel=\"noopener\" target=\"_blank\">toolbar<\/a> with undo\/redo and Zoom controls that can also affect the Gantt state. Even though it sits outside the chart, its actions don\u2019t need a separate state management algorithm. Clicking undo or changing zoom triggers the same update flow as any interaction inside the Gantt, ensuring state consistency.<\/p>\n<p>All React Gantt data handling follows a similar, predictable path. A user action first reaches <strong>data.save<\/strong>, which hands it off to the right atom. Just before anything is modified, <strong>pushHistory<\/strong> saves the current state. After that, the atom updates <strong>ganttStateAtom<\/strong>, and the new data is passed back into the Gantt, causing it to re-render. <\/p>\n<p>Now, we can proceed to the actual DHTMLX Gantt React integration with Jotai and consider atomic state management in React.<\/p>\n<h2>Prerequisites for Installation and Setup<\/h2>\n<p>If you want to run our demo, the complete setup instructions are provided in the project\u2019s repository. There are also a few more points worth noting.<\/p>\n<p>First of all, the demo uses the trial build (<strong>@dhtmlx\/trial-react-gantt<\/strong>) of DHTMLX React Gantt, which is ideal for evaluation. If you decide to add this integration to production apps, you should replace this option with <strong>@dhx\/react-gantt<\/strong> and update imports accordingly.<\/p>\n<p>Another point is that the project runs on React 19 with Vite and TypeScript. Jotai works well with React 18 and 19 and requires no Provider wrapper by default (provider-less mode).<\/p>\n<p>And there is a final CSS styling note related to Vite\u2019s default template. You will need to clear the default <strong>App.css<\/strong> styles and set <strong>height: 100%<\/strong> on <strong>#root<\/strong> for the Gantt to fill the viewport correctly.<\/p>\n<p>That\u2019s it. Let\u2019s get down to the most interesting part, namely, integration details that matter.<\/p>\n<h2>Implementation Walkthrough<\/h2>\n<p>We\u2019ve already covered the main building blocks of state management in this demo. Now, let\u2019s see how they are implemented in code and clarify a few important details <\/p>\n<h3>1. Storing Gantt data<\/h3>\n<p>As you already know from the above, the current Gantt state is stored in <strong>ganttStateAtom<\/strong>, which is specified as follows:<\/p>\n<div class=\"codecolorer-container javascript default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"javascript codecolorer\"><span class=\"kw5\">export<\/span> <span class=\"kw1\">const<\/span> ganttStateAtom <span class=\"sy0\">=<\/span> atom<span class=\"sy0\">&lt;<\/span>GanttState<span class=\"sy0\">&gt;<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#123;<\/span> &nbsp;<br \/>\n&nbsp; tasks<span class=\"sy0\">:<\/span> seedTasks<span class=\"sy0\">,<\/span> &nbsp;<br \/>\n&nbsp; links<span class=\"sy0\">:<\/span> seedLinks<span class=\"sy0\">,<\/span> &nbsp;<br \/>\n&nbsp; config<span class=\"sy0\">:<\/span> <span class=\"br0\">&#123;<\/span> zoom<span class=\"sy0\">:<\/span> defaultZoomLevels <span class=\"br0\">&#125;<\/span><span class=\"sy0\">,<\/span> &nbsp;<br \/>\n<span class=\"br0\">&#125;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span><br \/>\n<br \/>\n<span class=\"kw1\">const<\/span> maxHistory <span class=\"sy0\">=<\/span> <span class=\"nu0\">50<\/span><span class=\"sy0\">;<\/span><br \/>\n<br \/>\n<span class=\"kw5\">export<\/span> <span class=\"kw1\">const<\/span> pastAtom <span class=\"sy0\">=<\/span> atom<span class=\"sy0\">&lt;<\/span>GanttState<span class=\"br0\">&#91;<\/span><span class=\"br0\">&#93;<\/span><span class=\"sy0\">&gt;<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#91;<\/span><span class=\"br0\">&#93;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span> &nbsp;<br \/>\n<span class=\"kw5\">export<\/span> <span class=\"kw1\">const<\/span> futureAtom <span class=\"sy0\">=<\/span> atom<span class=\"sy0\">&lt;<\/span>GanttState<span class=\"br0\">&#91;<\/span><span class=\"br0\">&#93;<\/span><span class=\"sy0\">&gt;<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#91;<\/span><span class=\"br0\">&#93;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span><\/div><\/div>\n<p>Whenever something changes in the UI, this object gets updated, and the React Gantt re-renders based on it. <\/p>\n<p>Apart from that, this piece of code also includes <strong>pastAtom<\/strong> and <strong>futureAtom<\/strong>, which are required for handling undo\/redo history stacks. We limited the maximum history to 50 entries to prevent it from growing indefinitely, reducing the risk of associated memory consumption issues. We\u2019ll cover the undo\/redo mechanism in more detail a bit later.<\/p>\n<h3>2. Applying changes to the Gantt state<\/h3>\n<p>Now, when you know where the Gantt state \u201clives\u201d, the next question is how it actually changes. Every update to the Gantt goes through write-only atoms. Each of them is responsible for a single type of UI mutation (creating a task, deleting a dependency link, etc.).<\/p>\n<div class=\"codecolorer-container javascript default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"javascript codecolorer\"><span class=\"kw5\">export<\/span> <span class=\"kw1\">const<\/span> addTaskAtom <span class=\"sy0\">=<\/span> atom<span class=\"br0\">&#40;<\/span><span class=\"kw2\">null<\/span><span class=\"sy0\">,<\/span> <span class=\"br0\">&#40;<\/span><span class=\"kw1\">get<\/span><span class=\"sy0\">,<\/span> <span class=\"kw1\">set<\/span><span class=\"sy0\">,<\/span> task<span class=\"sy0\">:<\/span> SerializedTask<span class=\"br0\">&#41;<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"br0\">&#123;<\/span> &nbsp;<br \/>\n&nbsp; pushHistory<span class=\"br0\">&#40;<\/span><span class=\"kw1\">get<\/span><span class=\"sy0\">,<\/span> <span class=\"kw1\">set<\/span><span class=\"sy0\">,<\/span> <span class=\"kw1\">get<\/span><span class=\"br0\">&#40;<\/span>ganttStateAtom<span class=\"br0\">&#41;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span> &nbsp;<br \/>\n&nbsp; <span class=\"kw1\">set<\/span><span class=\"br0\">&#40;<\/span>ganttStateAtom<span class=\"sy0\">,<\/span> <span class=\"br0\">&#123;<\/span> &nbsp;<br \/>\n&nbsp; &nbsp; ...<span class=\"kw1\">get<\/span><span class=\"br0\">&#40;<\/span>ganttStateAtom<span class=\"br0\">&#41;<\/span><span class=\"sy0\">,<\/span> &nbsp;<br \/>\n&nbsp; &nbsp; tasks<span class=\"sy0\">:<\/span> <span class=\"br0\">&#91;<\/span>...<span class=\"kw1\">get<\/span><span class=\"br0\">&#40;<\/span>ganttStateAtom<span class=\"br0\">&#41;<\/span>.<span class=\"me1\">tasks<\/span><span class=\"sy0\">,<\/span> <span class=\"br0\">&#123;<\/span> ...<span class=\"me1\">task<\/span><span class=\"sy0\">,<\/span> id<span class=\"sy0\">:<\/span> `DB_ID<span class=\"sy0\">:<\/span>$<span class=\"br0\">&#123;<\/span>task.<span class=\"me1\">id<\/span><span class=\"br0\">&#125;<\/span>` <span class=\"br0\">&#125;<\/span><span class=\"br0\">&#93;<\/span><span class=\"sy0\">,<\/span> &nbsp;<br \/>\n&nbsp; <span class=\"br0\">&#125;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span> &nbsp;<br \/>\n&nbsp; <span class=\"kw1\">return<\/span> <span class=\"br0\">&#123;<\/span> ...<span class=\"me1\">task<\/span><span class=\"sy0\">,<\/span> id<span class=\"sy0\">:<\/span> `DB_ID<span class=\"sy0\">:<\/span>$<span class=\"br0\">&#123;<\/span>task.<span class=\"me1\">id<\/span><span class=\"br0\">&#125;<\/span>` <span class=\"br0\">&#125;<\/span><span class=\"sy0\">;<\/span> &nbsp;<br \/>\n<span class=\"br0\">&#125;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span><\/div><\/div>\n<p>Before anything is changed, the current state is passed into <strong>pushHistory<\/strong>. That\u2019s what later allows undo and redo to work. After that, the task is appended to <strong>ganttStateAtom<\/strong>. The same algorithm works for other operations with tasks and links.<\/p>\n<p>But how does the React component actually call them? Write-only atoms are used through Jotai\u2019s <strong>useSetAtom<\/strong> hook aimed for individual updates:<\/p>\n<div class=\"codecolorer-container javascript default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"javascript codecolorer\"><span class=\"kw1\">const<\/span> addTask <span class=\"sy0\">=<\/span> useSetAtom<span class=\"br0\">&#40;<\/span>addTaskAtom<span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span> <br \/>\n<span class=\"kw1\">const<\/span> updateTask <span class=\"sy0\">=<\/span> useSetAtom<span class=\"br0\">&#40;<\/span>updateTaskAtom<span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span><\/div><\/div>\n<p>This way, write-only atoms don\u2019t expose any value to read. You don\u2019t subscribe to them. They simply take an action and apply it to the state. <\/p>\n<p>Only <strong>ganttStateAtom<\/strong> is read via <strong>useAtom<\/strong>, because the React component needs the full state to pass it as props to <strong>ReactGantt<\/strong>.<\/p>\n<h3>3. Paving the way from UI actions to state updates<\/h3>\n<p>When a user interacts with the UI, Gantt reports what happened via <strong>data.save<\/strong> instead of updating anything itself. This callback is a single point where UI interactions can reach the state layer. Its main purpose is to translate user actions into calls to the corresponding state-changing atoms.<\/p>\n<p>For &#8220;create&#8221; operations, <strong>data.save<\/strong> returns the atom result back to the Gantt to keep IDs in sync. Let us elaborate a bit on this matter. When a task is created, the atom returns (via <strong>data.save<\/strong>) the task object with a new, store-assigned ID instead of the temporary one generated on the Gantt side. It helps avoid the issue with mismatched IDs. Finally, the data object is wrapped in <strong>useMemo<\/strong> to avoid recreating it on every render.<\/p>\n<p>To learn more details about the use of data.save, check the guide on <a href=\"https:\/\/docs.dhtmlx.com\/gantt\/integrations\/react\/state\/state-management-basics\/#handlingchangeswithdatasave\" rel=\"noopener\" target=\"_blank\">state management basics with DHTMLX React Gantt<\/a>.<\/p>\n<h3>4. Complementing React Gantt with undo\/redo functionality<\/h3>\n<p>In our demo, the Gantt component doesn\u2019t manage undo\/redo, the state layer takes care of it. The Jotai store keeps snapshots of the entire Gantt state and switches between them when needed. <\/p>\n<p>As you already know, the current Gantt state is saved before any update using <strong>pushHistory<\/strong>. Here is what happens next:<\/p>\n<div class=\"codecolorer-container javascript default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"javascript codecolorer\"><span class=\"kw1\">const<\/span> pushHistory <span class=\"sy0\">=<\/span> <span class=\"br0\">&#40;<\/span><span class=\"kw1\">get<\/span><span class=\"sy0\">:<\/span> Getter<span class=\"sy0\">,<\/span> <span class=\"kw1\">set<\/span><span class=\"sy0\">:<\/span> Setter<span class=\"sy0\">,<\/span> state<span class=\"sy0\">:<\/span> GanttState<span class=\"br0\">&#41;<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"br0\">&#123;<\/span> <br \/>\n&nbsp; <span class=\"kw1\">const<\/span> past <span class=\"sy0\">=<\/span> <span class=\"br0\">&#91;<\/span>...<span class=\"kw1\">get<\/span><span class=\"br0\">&#40;<\/span>pastAtom<span class=\"br0\">&#41;<\/span><span class=\"sy0\">,<\/span> state<span class=\"br0\">&#93;<\/span><span class=\"sy0\">;<\/span> <br \/>\n&nbsp; <span class=\"kw1\">if<\/span> <span class=\"br0\">&#40;<\/span>past.<span class=\"me1\">length<\/span> <span class=\"sy0\">&gt;<\/span> maxHistory<span class=\"br0\">&#41;<\/span> past.<span class=\"me1\">shift<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span> <br \/>\n&nbsp; <span class=\"kw1\">set<\/span><span class=\"br0\">&#40;<\/span>pastAtom<span class=\"sy0\">,<\/span> past<span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span> <br \/>\n&nbsp; <span class=\"kw1\">set<\/span><span class=\"br0\">&#40;<\/span>futureAtom<span class=\"sy0\">,<\/span> <span class=\"br0\">&#91;<\/span><span class=\"br0\">&#93;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span> <br \/>\n<span class=\"br0\">&#125;<\/span><span class=\"sy0\">;<\/span><\/div><\/div>\n<p>Each snapshot of the Gantt state is added to <strong>pastAtom<\/strong>, while <strong>futureAtom<\/strong> is cleared. If the list gets too long (exceeds the <strong>maxHistory<\/strong> value), the oldest entry is removed. <\/p>\n<p>Undo and redo operations are handled by <strong>undoAtom<\/strong> and <strong>redoAtom<\/strong>. One restores the last saved state from <strong>pastAtom<\/strong>, the other restores a state from <strong>futureAtom<\/strong>. Since snapshots capture the full GanttState, undo and redo always apply to the whole chart, regardless of the type of change.<\/p>\n<p>That\u2019s it. This information should be enough to get a clear picture of the integration process implemented in our demo and confidently try it in your own scenarios. For more information on the technical part, check out a practical <a href=\"https:\/\/docs.dhtmlx.com\/gantt\/integrations\/react\/state\/jotai\/\" rel=\"noopener\" target=\"_blank\">DHTMLX React Gantt tutorial with Jotai<\/a>.<\/p>\n<h2>Final Thoughts<\/h2>\n<p>Summarizing the above, Jotai feels like a good match for a React Gantt setup. Updates stay focused, and there is no need to re-render the entire UI every time a single change takes place, which is vital for data-intensive charts. Still, there\u2019s a limit to how far this scales. Once the state starts spreading across many parts of a larger app, a centralized approach like Redux Toolkit is a preferable option. <\/p>\n<h2>Related Materials<\/h2>\n<ul>\n<li><a href=\"https:\/\/docs.dhtmlx.com\/gantt\/integrations\/react\/\" rel=\"noopener\" target=\"_blank\">DHTMLX React Gantt documentation<\/a><\/li>\n<li><a href=\"https:\/\/www.npmjs.com\/package\/@dhtmlx\/trial-react-gantt\" rel=\"noopener\" target=\"_blank\">React Gantt NPM package (trial version)<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/DHTMLX\/react-gantt-jotai-starter\" rel=\"noopener\" target=\"_blank\">DHTMLX React Gantt + Jotai (GitHub demo)<\/a><\/li>\n<li><a href=\"https:\/\/docs.dhtmlx.com\/gantt\/integrations\/react\/state\/jotai\/\" rel=\"noopener\" target=\"_blank\">DHTMLX React Gantt + Jotai (tutorial)<\/a><\/li>\n<li><a href=\"https:\/\/dhtmlx.com\/blog\/tutorials\/?tag=react\" rel=\"noopener\" target=\"_blank\">Other DHTMLX React tutorials<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>It is always useful to have more than one way to approach challenging tasks in web development, particularly state management in React apps. It is especially true for projects &#8230;<\/p>\n","protected":false},"author":101014,"featured_media":15476,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[71],"tags":[33,20,56],"class_list":["post-15475","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials","tag-all-posts","tag-gantt","tag-react"],"_links":{"self":[{"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/posts\/15475","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/users\/101014"}],"replies":[{"embeddable":true,"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/comments?post=15475"}],"version-history":[{"count":1,"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/posts\/15475\/revisions"}],"predecessor-version":[{"id":15478,"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/posts\/15475\/revisions\/15478"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/media\/15476"}],"wp:attachment":[{"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/media?parent=15475"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/categories?post=15475"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dhtmlx.com\/blog\/wp-json\/wp\/v2\/tags?post=15475"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}