<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by JavaScript UI Libraries — DHTMLX on Medium]]></title>
        <description><![CDATA[Stories by JavaScript UI Libraries — DHTMLX on Medium]]></description>
        <link>https://medium.com/@dhtmlx?source=rss-e9c0a531d5e5------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*gjV-CnImLGgFu5U47b3Egw.png</url>
            <title>Stories by JavaScript UI Libraries — DHTMLX on Medium</title>
            <link>https://medium.com/@dhtmlx?source=rss-e9c0a531d5e5------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 06 Jun 2026 11:21:48 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@dhtmlx/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Structuring React Gantt State with Jotai’s Atomic Model]]></title>
            <link>https://dhtmlx.medium.com/structuring-react-gantt-state-with-jotais-atomic-model-6531ea441811?source=rss-e9c0a531d5e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/6531ea441811</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[gantt-chart]]></category>
            <category><![CDATA[jotai]]></category>
            <category><![CDATA[state-management]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[JavaScript UI Libraries — DHTMLX]]></dc:creator>
            <pubDate>Thu, 28 May 2026 09:02:24 GMT</pubDate>
            <atom:updated>2026-05-28T09:02:24.094Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Hr_irKVpxBZleAeSJSB72A.jpeg" /></figure><p>State handling in React tends to get complicated the moment an application grows beyond a few isolated components. With project planning interfaces, the amount of interconnected UI behavior increases very quickly.</p><p>Previously, we explored several ways to organize this kind of logic, including <a href="https://dhtmlx.com/blog/using-dhtmlx-gantt-chart-for-react-with-redux-toolkit-principles-benefits-and-implementation-tips/">Redux Toolkit</a>, <a href="https://dhtmlx.com/blog/using-zustand-state-management-apps-dhtmlx-react-gantt-scheduler/">Zustand</a>, <a href="https://dhtmlx.com/blog/managing-state-dhtmlx-react-gantt-chart-scheduler-mobx/">MobX</a>, and <a href="https://dhtmlx.com/blog/using-xstate-in-react-gantt-and-scheduler-apps-for-complex-state-scenarios/">XState</a>. Jotai approaches the problem differently. Instead of pushing everything into one global store, it breaks the state into isolated atomic units. To demonstrate how that model behaves in practice, we’ll use the <a href="https://github.com/DHTMLX/react-gantt-jotai-starter">react-gantt-jotai-starter</a> project built with <a href="https://dhtmlx.com/docs/products/dhtmlxGantt-for-React/">DHTMLX React Gantt</a>.</p><p>Before moving into the implementation details, it’s worth understanding why Jotai feels noticeably different from more traditional React state libraries.</p><h3>Understanding Jotai’s Atomic Model</h3><p>Jotai was introduced as an alternative to the common <strong>useContext +</strong> <strong>useState</strong> pattern that many React teams eventually outgrow. In larger applications, context-based state often causes broader component updates than expected, and tracking why something re-rendered becomes frustrating surprisingly fast.</p><p>Jotai tries to reduce that friction by keeping the state extremely granular. The library gained traction partly because it stays very close to React’s native hooks philosophy instead of introducing a large architectural layer on top of it. That simplicity is one of the reasons many companies adopted it in production environments.</p><p>People often compare Jotai to Zustand because both libraries avoid heavy configuration and excessive boilerplate. The real distinction appears in how they organize data internally.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JzUH_28fVgHoRxdFroTafw.gif" /><figcaption>Source: State of React 2025</figcaption></figure><p>With Zustand, state typically lives inside a shared store that can later be divided into smaller slices. Jotai removes the idea of a mandatory central store entirely. Instead, every piece of state exists as its own atom. That architectural difference changes how updates propagate through the UI.</p><p>A component subscribes only to the atoms it actually consumes. When one atom changes, unrelated parts of the interface remain untouched. In data-heavy interfaces, this usually leads to more controlled rendering behavior and fewer optimization workarounds.</p><p>The API also stays fairly flexible. Atoms can be combined, derived from one another, or used purely for updates without exposing readable values. Because of that, the same model works both for lightweight local interactions and for more involved application flows.</p><p>Jotai also plays nicely with newer React capabilities, including Suspense and concurrent rendering patterns, which makes it feel relatively future-proof in modern React setups. The trade-off becomes noticeable later.</p><p>As applications expand, the number of atoms can grow rapidly. At that stage, organization becomes critical. Without consistent naming and structure, an atom-based architecture can become just as difficult to navigate as an oversized global store.</p><h3>Why Jotai Works Naturally with React Gantt Interfaces</h3><p>Interactive Gantt components behave differently from ordinary CRUD-style UIs. A seemingly small action like dragging a task along the timeline often triggers several related updates throughout the chart.</p><p>That constant chain reaction is exactly where Jotai starts to feel practical. Because updates are isolated into narrowly scoped atoms, changes remain localized. Rescheduling one task does not force unrelated parts of the application to refresh or recalculate unnecessarily. The update affects only the pieces directly connected to that interaction.</p><p>In practice, this makes state transitions easier to follow. Instead of tracing changes across reducers, middleware, or deeply nested state objects, you usually deal with a much smaller update surface. That becomes especially valuable once a project timeline grows and interactions happen continuously.</p><p>To see why this structure stays manageable, it helps to examine the overall data flow before diving into implementation details.</p><h3>Organizing Data Flow Around Atoms</h3><p>In this setup, the core Gantt data is stored inside a primary atom that acts as the authoritative state source for the chart. Tasks, dependency links, configuration values, and related UI state all live there.</p><p>Updates, however, are intentionally separated. Rather than mutating the main state directly, individual write-only atoms handle specific operations independently. One atom processes task creation, another updates links, another adjusts zoom configuration, and so forth.</p><p>The React Gantt component itself remains mostly passive. Its responsibility is simply to read current state values and pass them into DHTMLX React Gantt through props. When the user interacts with the chart, those interactions are routed through <strong>data.save</strong>, which delegates the update to the appropriate atom.</p><p>What’s important is that the same mechanism applies outside the chart as well. Toolbar actions such as undo, redo, or zoom changes do not introduce separate synchronization logic. They follow the identical update pipeline as interactions originating directly inside the Gantt UI.</p><p>A simplified flow looks roughly like this:</p><p><strong>user interaction → data.save intercepts the change → matching write-only atom executes → history snapshot updates → main Gantt atom changes → React Gantt re-renders with new state</strong></p><p>That consistency is what keeps the architecture understandable once the interface becomes more complex.</p><h3>How the Integration Works in Practice</h3><p>Let’s move from structure to actual behavior and look at how state updates are implemented.</p><h4>1. Keeping Gantt Data Inside Atoms</h4><p>The main state atom holds the full dataset, including tasks, links, and configuration (zoom, etc.):</p><pre>export const ganttStateAtom = atom&lt;GanttState&gt;({  <br>  tasks: seedTasks,  <br>  links: seedLinks,  <br>  config: { zoom: defaultZoomLevels },  <br>});<br><br>const maxHistory = 50;<br><br>export const pastAtom = atom&lt;GanttState[]&gt;([]);  <br>export const futureAtom = atom&lt;GanttState[]&gt;([]);</pre><p>Alongside it, there are two additional atoms: <strong>pastAtom</strong> and <strong>futureAtom</strong>. These are used for undo/redo history.</p><p>To prevent unbounded memory growth, history is capped (for example, at 50 entries). Older snapshots are dropped once the limit is reached.</p><h4>2. Applying Updates Through Write-Only Atoms</h4><p>All modifications happen through dedicated write-only atoms. Each one represents a specific mutation: create, update, or delete.</p><pre>export const addTaskAtom = atom(null, (get, set, task: SerializedTask) =&gt; {  <br>  pushHistory(get, set, get(ganttStateAtom));  <br>  set(ganttStateAtom, {  <br>    ...get(ganttStateAtom),  <br>    tasks: [...get(ganttStateAtom).tasks, { ...task, id: `DB_ID:${task.id}` }],  <br>  });  <br>  return { ...task, id: `DB_ID:${task.id}` };  <br>});</pre><p>Before applying any change, the current state is pushed into history. This ensures undo/redo works reliably later.</p><p>After that, the atom updates <strong>ganttStateAtom</strong> with a new immutable state.</p><p>These atoms are accessed via <strong>useSetAtom</strong>, which allows triggering updates without subscribing to their values. Only the main state atom is read by the UI.</p><h4>3. Translating UI Actions into State Changes</h4><p>User interactions inside DHTMLX React Gantt are not handled directly in the UI layer. Instead, everything goes through <strong>data.save</strong>. This function acts as a bridge between the visual layer and the state layer.</p><p>When a task is created, updated, or removed, the callback translates that action into the corresponding atom call. In creation flows, the updated entity (often with a generated ID) is returned to the Gantt to keep identifiers consistent.</p><p>This small detail prevents common synchronization issues between UI-generated IDs and store-generated IDs.</p><h4>4. Handling Undo and Redo Outside the UI Layer</h4><p>Undo/redo is implemented entirely outside the component layer. Each state change is recorded before mutation. The history stack stores full snapshots of the Gantt state.</p><ul><li><strong>pastAtom</strong> holds previous states</li><li><strong>futureAtom</strong> holds states for redo</li></ul><p>When undo is triggered, the last snapshot is restored and moved into the future stack. Redo performs the opposite operation.</p><p>Because snapshots capture the entire state, undo/redo remains consistent across all types of changes.</p><p>You can continue mastering integration patterns of DHTMLX React Gantt with Jotai using the <a href="https://docs.dhtmlx.com/gantt/integrations/react/state/jotai/">official DHTMLX tutorial</a>.</p><h3>Closing Remarks</h3><p>Jotai fits surprisingly well into a Gantt-based workflow. Its atomic structure keeps updates localized, which is important when working with highly interactive interfaces like DHTMLX React Gantt.</p><p>That said, it’s not a universal solution. As applications scale and the number of atoms grows, maintaining a clear structure becomes a responsibility of the developer rather than the library.</p><p>In smaller or medium-sized systems, it feels lightweight and direct. In larger enterprise setups, centralized approaches can still make more sense depending on the complexity of the shared state.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6531ea441811" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Reducing AI Guesswork in React Gantt Projects with a New Agent Skill]]></title>
            <link>https://ai.plainenglish.io/reducing-ai-guesswork-in-react-gantt-projects-with-a-new-agent-skill-842cb83df9a3?source=rss-e9c0a531d5e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/842cb83df9a3</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[gantt-chart]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[skills]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[JavaScript UI Libraries — DHTMLX]]></dc:creator>
            <pubDate>Tue, 12 May 2026 09:03:10 GMT</pubDate>
            <atom:updated>2026-05-22T21:58:52.693Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YsjeMucNcTGpWxH47gHAoA.jpeg" /></figure><p>AI coding assistants have already become part of everyday frontend work. Generating repetitive React code, scaffolding components, or wiring basic logic is no longer the hard part. Problems usually begin later, when the model has to interact with a complex UI library that it does not fully understand.</p><p>That situation is pretty common with project-planning interfaces. A Gantt component is not just another visual widget. It comes with its own data model, interaction patterns, configuration rules, and API specifics. Without proper context, AI tools tend to compensate with assumptions, and the results become unpredictable surprisingly fast.</p><p>To make this workflow more stable for DHTMLX users, we started building agent skills focused on specific products instead of generic prompting tricks.</p><p>The first implementation targets <a href="https://dhtmlx.com/docs/products/dhtmlxGantt-for-React/">DHTMLX React Gantt</a>.</p><h3>How the Skill Changes AI-Assisted Gantt Development</h3><p>The idea behind the <a href="https://docs.dhtmlx.com/gantt/integrations/ai-tools/agent-skills/">React Gantt skill</a> is straightforward: provide AI assistants with clearer operational knowledge of how the component is typically used in React applications.</p><p>Instead of assembling integrations from random examples scattered across the web, the model receives structured guidance tailored specifically for both available package versions - trial (@dhtmlx/trial-react-gantt) and complete (@dhx/react-gantt).</p><p>The skill focuses on areas where AI-generated implementations usually become inconsistent:</p><ul><li>configuring the Gantt component inside React</li><li>handling task and link operations</li><li>applying visual customizations and themes</li><li>implementing more advanced behaviors like resources, undo/redo flows, or task reordering</li></ul><p>In practice, AI coding errors around UI libraries rarely look dramatic at first. More often, the assistant quietly mixes incompatible APIs, invents configuration options, or reproduces patterns from outdated examples. Those issues are especially annoying because they can survive code review until runtime.</p><p>The React Gantt skill is meant to reduce that kind of friction. It provides implementation-oriented instructions based on real integration scenarios and can additionally validate uncertain API details through the DHTMLX MCP infrastructure.</p><p>The result is not “magic AI coding,” but something more useful: responses that stay much closer to real-world React Gantt implementations.</p><p>Installing the skill takes a single command:</p><pre>npx skills add DHTMLX/skills - skill dhtmlx-react-gantt</pre><p>It can be used with AI development tools supporting the Agent Skills format, including Claude Code, Cursor, Copilot, and related environments.</p><p>Another practical detail: the entire skill definition is stored in readable Markdown files. That means developers can inspect the rules directly, adapt them internally, or simply understand why the assistant behaves a certain way during integration tasks.</p><h3>Why Live Documentation Still Matters</h3><p>Even detailed implementation rules have limitations. Frontend libraries evolve continuously. APIs shift, new options appear, old approaches become deprecated, and examples written several months ago may already contain inaccuracies. AI assistants struggle with this particularly hard because their internal knowledge is static by nature.</p><p>That is exactly the problem the <a href="https://dhtmlx.com/docs/products/dhtmlx-mcp-server/">DHTMLX MCP Server</a> is designed to address.</p><p>The React Gantt skill helps the assistant follow correct integration patterns, while the MCP server supplies current technical references directly from the latest DHTMLX documentation. Instead of improvising around uncertain APIs, the model can retrieve relevant information from a live source.</p><p>From a development perspective, these two layers solve different problems:</p><ul><li>the skill improves behavioral consistency</li><li>the MCP server improves factual accuracy</li></ul><p>Combined, they make AI-assisted development around DHTMLX React Gantt feel considerably more dependable than prompt-only workflows.</p><p>The setup itself is lightweight, and the official documentation covers the installation process step by step.</p><p>React Gantt is only the starting point here. The same direction will gradually extend to other DHTMLX components as well, especially where AI tools tend to struggle with library-specific implementation details.</p><h3>A message from our Founder</h3><p>Hey, <a href="https://linkedin.com/in/sunilsandhu">Sunil</a> here. I wanted to take a moment to thank you for reading until the end and for being a part of this community. Did you know that our team run these publications as a volunteer effort to over 3.5m monthly readers? We don’t receive any funding, we do this to support the community.</p><p>If you want to show some love, please take a moment to follow me on <a href="https://linkedin.com/in/sunilsandhu">LinkedIn</a>, <a href="https://tiktok.com/@messyfounder">TikTok</a>, <a href="https://instagram.com/sunilsandhu">Instagram</a>. You can also subscribe to our <a href="https://newsletter.plainenglish.io/">weekly newsletter</a>. And before you go, don’t forget to clap and follow the writer️!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=842cb83df9a3" width="1" height="1" alt=""><hr><p><a href="https://ai.plainenglish.io/reducing-ai-guesswork-in-react-gantt-projects-with-a-new-agent-skill-842cb83df9a3">Reducing AI Guesswork in React Gantt Projects with a New Agent Skill</a> was originally published in <a href="https://ai.plainenglish.io">Artificial Intelligence in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Managing Complex UI Logic in React Gantt and Scheduler with XState]]></title>
            <link>https://dhtmlx.medium.com/managing-complex-ui-logic-in-react-gantt-and-scheduler-with-xstate-bd34ca4969b4?source=rss-e9c0a531d5e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/bd34ca4969b4</guid>
            <category><![CDATA[state-management]]></category>
            <category><![CDATA[xstate]]></category>
            <category><![CDATA[reactjs]]></category>
            <category><![CDATA[gantt-chart]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[JavaScript UI Libraries — DHTMLX]]></dc:creator>
            <pubDate>Wed, 22 Apr 2026 14:32:07 GMT</pubDate>
            <atom:updated>2026-04-22T14:41:04.021Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KNwKL6h2faIFPHYknI9M_g.png" /></figure><p>State management in a React Gantt app rarely stays simple for long. Once real workflows kick in, updates start triggering other updates, and keeping everything in sync becomes less predictable.</p><p>We’ve tried <a href="https://dhtmlx.com/blog/using-dhtmlx-gantt-chart-for-react-with-redux-toolkit-principles-benefits-and-implementation-tips/">Redux Toolkit</a>, <a href="https://dhtmlx.com/blog/using-zustand-state-management-apps-dhtmlx-react-gantt-scheduler/">Zustand</a>, and <a href="https://dhtmlx.com/blog/managing-state-dhtmlx-react-gantt-chart-scheduler-mobx/">MobX</a> with DHTMLX components, which are solid tools, but all built around the same idea of directly updating state.</p><p>XState flips that perspective. Instead of changing state, you describe how it’s allowed to change. Let’s see what that looks like in React Gantt and Scheduler projects.</p><h3>Understanding XState Through Real React Use Cases</h3><p>XState usually appears in a project at a very specific moment, not at the start, but when state logic becomes annoying to track.</p><p>You start noticing it when simple updates aren’t enough anymore. A task update triggers something else. A UI state depends on multiple async steps. Things stop being local and start affecting each other.</p><p>It works fine in plain JavaScript and integrates with React without friction, but it’s rarely introduced early because the mental shift is real.</p><p>The core idea is finite state machines. Sounds theoretical, but in practice it’s more restrictive than complex: you define allowed states and explicitly describe how transitions happen between them. Nothing “just changes” anymore. That restriction is the point. The image below clearly illustrates the core ideas behind state machines in XState.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/828/1*OuQnqYA3Mv4G77jDMgnSqg.png" /><figcaption><em>Source: XState</em></figcaption></figure><p>Every change comes from an event, usually something user-driven. Once that event happens, XState runs actions that update context (its internal state container). Instead of spreading logic across components, everything gets centralized in one place.</p><p>As applications grow, simple state machines don’t always stay simple. That’s where statecharts come in — nested states, parallel flows, separated concerns inside one system. This becomes useful when the UI isn’t doing one thing at a time anymore.</p><p>And when even that isn’t enough, XState introduces actors — separate machines running independently and communicating. It sounds heavy at first, but in larger apps, it often reduces confusion rather than adding to it.</p><p>The end result is less about “managing state” and more about defining allowed behavior.</p><p>Instead of reacting to whatever state happens to be, you define what <em>can</em> happen. That removes entire classes of bugs where the UI ends up in impossible combinations.</p><p>There’s also a broader ecosystem around it, known as the Stately tools, mostly focused on visualizing and inspecting machines. That becomes useful once systems stop fitting in your head.</p><p>We won’t go deeper into theory here. The more interesting question is where this actually helps compared to other approaches.</p><h3>When XState Actually Makes Sense</h3><p>XState is not something most teams pick by default. Most React apps start with Redux Toolkit, Zustand, or MobX because the flow is familiar: update state, UI reacts, done.</p><p>XState doesn’t follow that pattern. You don’t “update state” directly; you describe behavior. That difference takes a bit of adjustment.</p><p>In small apps, it can feel unnecessary. There’s no benefit in modeling everything as a machine if the logic is simple. Where it starts to make sense is when things stop being linear. That usually happens in real-world interfaces with overlapping processes: async operations, dependent updates, and multiple UI states influencing each other.</p><p>This is exactly what shows up in tools like <a href="https://dhtmlx.com/docs/products/dhtmlxGantt-for-React/">DHTMLX React Gantt</a> or <a href="https://dhtmlx.com/docs/products/dhtmlxScheduler-for-React/">DHTMLX React Scheduler</a>.</p><p>At that point, XState stops being “a state library” and becomes a way to control how the system behaves over time.</p><p>We’ll skip architecture discussions and focus on a real integration example instead.</p><h3>Building a Gantt App with XState: Main Integration Steps</h3><p>The integration flow itself isn’t radically different from other state tools. What changes is how the state is represented. With XState, you’re no longer wiring a store, you’re defining a machine.</p><p>We’ll focus on DHTMLX React Gantt, but the same approach applies to DHTMLX React Scheduler.</p><p>If you want to try this setup in your own project, we’ve put together a step-by-step guide for integrating <a href="https://docs.dhtmlx.com/gantt/integrations/react/state/xstate/">XState with DHTMLX React Gantt</a> and a similar walkthrough for <a href="https://docs.dhtmlx.com/scheduler/integrations/react/state/xstate/">React Scheduler</a>. If you prefer to see the full setup in action, there’s also a complete <a href="https://github.com/dhtmlx/react-gantt-xstate-starter">Gantt demo project on GitHub</a>.</p><h4>Building the XState Machine Structure</h4><p>Everything starts with defining what the machine actually holds. Before transitions or events, you define the context shape or what lives inside the system.</p><pre>import { createMachine, assign } from &#39;xstate&#39;;  <br>import type { Link, GanttConfig, SerializedTask } from &#39;@dhtmlx/trial-react-gantt&#39;;  <br>import { seedTasks, seedLinks, defaultZoomLevels, type ZoomLevel } from &#39;./seed/Seed&#39;;<br><br>export interface Snapshot {  <br>  tasks: SerializedTask[];  <br>  links: Link[];  <br>  config: GanttConfig;  <br>}<br><br>export interface ContextType {  <br>  tasks: SerializedTask[];  <br>  links: Link[];  <br>  config: GanttConfig;<br><br>  past: Snapshot[];  <br>  future: Snapshot[];  <br>  maxHistory: number;  <br>}</pre><p>What matters here isn’t syntax. It’s separation. <strong>Context</strong> holds the live state (tasks, links, config, history). <strong>Snapshot</strong> exists for one reason: so undo/redo doesn’t turn into guesswork. Instead of reconstructing previous states, you store them explicitly. That small decision makes history logic much easier to reason about later.</p><h4>How State Changes Are Defined</h4><p>Once the structure is in place, things shift from modeling to interaction. Now it’s about what happens when the user actually does something.</p><p>With XState, every meaningful interaction becomes an event. In a Gantt app, that’s basically everything (task edits, link updates, zoom changes, undo/redo actions).</p><p>So instead of spreading logic across components, you describe events explicitly:</p><pre>type SetZoomEvent = { type: &#39;SET_ZOOM&#39;; level: ZoomLevel };  <br>type UndoEvent = { type: &#39;UNDO&#39; };  <br>type RedoEvent = { type: &#39;REDO&#39; };  <br>type AddTaskEvent = { type: &#39;ADD_TASK&#39;; task: SerializedTask };  <br>type UpsertTaskEvent = { type: &#39;UPSERT_TASK&#39;; task: SerializedTask };  <br>type DeleteTaskEvent = { type: &#39;DELETE_TASK&#39;; id: string | number };  <br>type AddLinkEvent = { type: &#39;ADD_LINK&#39;; link: Link };  <br>type UpsertLinkEvent = { type: &#39;UPSERT_LINK&#39;; link: Link };  <br>type DeleteLinkEvent = { type: &#39;DELETE_LINK&#39;; id: string | number };<br><br>type EventType =  <br>  | SetZoomEvent  <br>  | UndoEvent  <br>  | RedoEvent  <br>  | AddTaskEvent  <br>  | UpsertTaskEvent  <br>  | DeleteTaskEvent  <br>  | AddLinkEvent  <br>  | UpsertLinkEvent  <br>  | DeleteLinkEvent;</pre><p>Nothing fancy here, just a list of possible actions in the system. But it changes the way you think about UI: nothing happens implicitly anymore. From there, each event maps to an action that updates the state.</p><pre>addTask: assign(({ context: ctx, event }) =&gt; ({  <br>  tasks: [...ctx.tasks, { ...(event as AddTaskEvent).task, id: `DB_ID:${(event as AddTaskEvent).task.id}` }],  <br>})),<br><br>upsertTask: assign(({ context: ctx, event }) =&gt; ({  <br>  tasks: ctx.tasks.map((task) =&gt;  <br>    String(task.id) === String((event as UpsertTaskEvent).task.id)  <br>      ? { ...task, ...(event as UpsertTaskEvent).task }  <br>      : task  <br>  ),  <br>})),<br><br>deleteTask: assign(({ context, event }) =&gt; ({  <br>  tasks: context.tasks.filter((t) =&gt; String(t.id) !== String((event as DeleteTaskEvent).id)),  <br>})),</pre><p>After a few of these, a pattern emerges. It’s repetitive, but intentionally so. The important shift is that the state is no longer updated directly. Everything goes throughassign, which builds a new context from the current state and the event.</p><p>That alone changes debugging behavior. You stop searching for hidden mutations and instead follow event flow.</p><h4>Creating the Machine Configuration</h4><p>At some point, everything gets wired together — context, events, and actions inside one machine.</p><p>Before that, there’s one detail worth calling out <strong>snapshots</strong>:</p><pre>const createSnapshot = (ctx: ContextType): Snapshot =&gt; ({  <br>  tasks: structuredClone(ctx.tasks),  <br>  links: structuredClone(ctx.links),  <br>  config: structuredClone(ctx.config),  <br>});</pre><p>Not exciting code, but without it, undo/redo becomes unreliable quickly. Now the machine itself:</p><pre>export const ganttMachine = createMachine(  <br>  {  <br>    id: &#39;gantt&#39;,  <br>    types: {  <br>      context: {} as ContextType,  <br>      events: {} as EventType,  <br>    },  <br>    context: {  <br>      tasks: seedTasks,  <br>      links: seedLinks,  <br>      config: { zoom: defaultZoomLevels },  <br>      past: [],  <br>      future: [],  <br>      maxHistory: 50,  <br>    },  <br>    initial: &#39;ready&#39;,  <br>    states: {  <br>      ready: {  <br>        on: {  <br>          SET_ZOOM: { actions: [&#39;pushHistory&#39;, &#39;setZoom&#39;] },  <br>          UNDO: { actions: &#39;undo&#39; },  <br>          REDO: { actions: &#39;redo&#39; },<br><br>          ADD_TASK: { actions: [&#39;pushHistory&#39;, &#39;addTask&#39;] },  <br>          UPSERT_TASK: { actions: [&#39;pushHistory&#39;, &#39;upsertTask&#39;] },  <br>          DELETE_TASK: { actions: [&#39;pushHistory&#39;, &#39;deleteTask&#39;] },<br><br>          ADD_LINK: { actions: [&#39;pushHistory&#39;, &#39;addLink&#39;] },  <br>          UPSERT_LINK: { actions: [&#39;pushHistory&#39;, &#39;upsertLink&#39;] },  <br>          DELETE_LINK: { actions: [&#39;pushHistory&#39;, &#39;deleteLink&#39;] },  <br>        },  <br>      },  <br>    },  <br>  },<br>)</pre><p>There’s only one real state here, namelyready. That’s intentional. The machine acts more like a controlled dispatcher than a multi-state workflow. Every event and action goes through it. Compared to Redux Toolkit or Zustand, where you call functions directly, this feels more indirect at first. But that’s also what makes it traceable. You don’t mutate anything manually. You send an event, and the machine decides what happens next.</p><h4>Wiring Gantt UI Actions to the State Machine</h4><p>This is where UI finally connects to the machine. In DHTMLX React Gantt, everything flows through data.save.</p><pre>const data: ReactGanttProps[&#39;data&#39;] = useMemo(  <br>  () =&gt; ({  <br>    save: (entity, action, item, id) =&gt; {  <br>      if (entity === &#39;task&#39;) {  <br>        const task = item as SerializedTask;  <br>        if (action === &#39;create&#39;) {  <br>          send({ type: &#39;ADD_TASK&#39;, task });  <br>        } else if (action === &#39;update&#39;) {  <br>          send({ type: &#39;UPSERT_TASK&#39;, task });  <br>        } else if (action === &#39;delete&#39;) {  <br>          send({ type: &#39;DELETE_TASK&#39;, id });  <br>        }  <br>      } else if (entity === &#39;link&#39;) {  <br>        const link = item as Link;  <br>        if (action === &#39;create&#39;) {  <br>          send({ type: &#39;ADD_LINK&#39;, link });  <br>        } else if (action === &#39;update&#39;) {  <br>          send({ type: &#39;UPSERT_LINK&#39;, link });  <br>        } else if (action === &#39;delete&#39;) {  <br>          send({ type: &#39;DELETE_LINK&#39;, id });  <br>        }  <br>      }  <br>    },  <br>  }),  <br>  [send]  <br>);</pre><p>Each user interaction hits this callback first. Instead of updating the state locally, we just send an event into XState. After that, the UI doesn’t decide anything. It just reports what happened.</p><p>The Gantt component itself stays intentionally “dumb”; it doesn’t own state, doesn’t mutate anything. It only renders props. That separation is what keeps behavior predictable when the app grows.</p><p>The full loop looks like this:</p><p><strong>User action → data.save → event sent → machine processes it → context updates → props update → UI re-renders</strong></p><p>The same idea applies to DHTMLX React Scheduler.</p><h3>Conclusion</h3><p>XState isn’t something you reach for by default. In smaller apps, it often feels like unnecessary structure. A bit heavy, a bit rigid. But once UI logic stops being linear, especially in systems like Gantt charts or schedulers, things change. State stops being just data. It becomes behavior over time. In those cases, modeling transitions explicitly tends to reduce uncertainty as the system grows. Not simpler upfront, but usually more stable later. Still, it’s not a universal choice. If the problem doesn’t need that level of structure, XState is probably more than you want.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bd34ca4969b4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Scaling AI Knowledge Systems: Lessons from the DHTMLX MCP Server]]></title>
            <link>https://ai.plainenglish.io/scaling-ai-knowledge-systems-lessons-from-the-dhtmlx-mcp-server-6d3de12a5901?source=rss-e9c0a531d5e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/6d3de12a5901</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[mcp-server]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[ai]]></category>
            <dc:creator><![CDATA[JavaScript UI Libraries — DHTMLX]]></dc:creator>
            <pubDate>Tue, 17 Mar 2026 17:19:23 GMT</pubDate>
            <atom:updated>2026-03-19T01:39:30.534Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nbD9cvUAKfsb7fwhW5PJDA.png" /></figure><p>Not long ago, we introduced the <a href="https://dhtmlx.com/docs/products/dhtmlx-mcp-server/">DHTMLX MCP Server</a>–a centralized knowledge layer that gives AI assistants and developer tools structured access to the latest documentation across the entire DHTMLX product line, from Suite widgets to Gantt and Scheduler.</p><p>Behind that idea, however, was a fairly tricky engineering problem. It’s one thing to build a knowledge system for a single product. It’s something else entirely to make it work across a full ecosystem without losing accuracy or context.</p><p>At the core of the MCP server is the Retrieval-Augmented Generation (RAG) approach. It pulls relevant fragments from documentation and passes them to LLMs to generate responses grounded in real data. This setup works reliably when you’re dealing with one product. But once multiple products enter the picture, the complexity increases quickly.</p><h3>RAG and MCP: What’s Going On Under the Hood</h3><p>Before getting into implementation details, it helps to align on the basics behind RAG and MCP, since most of the architecture builds on these two ideas.</p><p>So how do you actually turn a Retrieval-Augmented Generation setup into an MCP server?</p><p>At a high level, RAG is about giving an LLM access to external knowledge instead of relying purely on its training data. The process usually breaks down into three steps:</p><ul><li>find relevant pieces of external data (retrieval)</li><li>add that context to the prompt (augmentation)</li><li>produce a response based on that context (generation)</li></ul><p>In practice, the pipeline looks fairly standard. You take your documentation, split it into chunks, convert each chunk into embeddings, and store them in an index. When a query comes in, the system looks for the closest matches using similarity metrics like cosine similarity, then feeds those results into the model.</p><p>Now, where does MCP fit into this?</p><p>The Model Context Protocol acts as a kind of universal connector between AI agents and external systems. A common analogy is a “USB-C port” for AI apps, one interface that can plug into different types of data and functionality.</p><p>Through MCP, an AI system can work with:</p><ul><li>things like API calls or database queries (tools)</li><li>documentation, structured content, reusable logic (resources)</li><li>predefined workflows or few-shot examples (prompts)</li></ul><p>In other words, MCP turns an LLM from a static knowledge source into something that can operate with live, structured context.</p><p>So what does it take to build a system like this?</p><p>In practice, MCP servers with RAG capabilities are often built using a combination of frameworks. A few commonly used ones:</p><ul><li>FastMCP — typically used to implement the MCP layer</li><li>LlamaIndex — well-suited for knowledge-heavy RAG pipelines</li><li>LangChain — often used for agent-style workflows</li></ul><p>There’s also a common misconception that this stack is strictly Python-based. In reality, both LlamaIndex and LangChain support JavaScript, and MCP itself is language-agnostic with SDKs across different ecosystems.</p><p>At first glance, assembling a RAG + MCP setup looks fairly straightforward: pick the tools, connect the pieces, and you’re done.</p><p>But that assumption starts to break once you move beyond a single product. Supporting multiple products, each with its own documentation structure and API surface, introduces a different level of complexity. At that point, the real question becomes not how to build the system, but how to organize the knowledge behind it.</p><h3>Moving Toward a Machine Learning Approach</h3><p>When you first try to scale a Retrieval-Augmented Generation system, the obvious idea is to put everything into a single index. From an infrastructure standpoint, it feels clean and efficient, one place for all product knowledge.</p><p>In practice, though, that approach breaks down pretty quickly.</p><p><a href="https://dhtmlx.com/docs/products/">DHTMLX JavaScript UI components</a> follow very different internal logic. Once you mix their documentation into the same vector space, the system starts to blur the boundaries between them. You might ask about one component and get suggestions that clearly belong to another. API methods get mixed up, examples don’t quite fit, and overall answer quality takes a hit.</p><p>The natural fix was to split the knowledge base. We moved to separate indexes for each product, which immediately improved accuracy within each domain.</p><p>But that introduced a new problem.</p><p>Now the system had to decide where to send each query before retrieval even begins. In other words, we needed a reliable way to map a user’s question to the right product.</p><p>At first, the usual approaches seemed good enough. Things like keyword matching, regular expressions, or fuzzy search are easy to implement. You can also route queries through an LLM and let it classify intent.</p><p>In reality, none of these options held up well:</p><ul><li><strong>heuristic methods</strong> (keywords, regex, fuzzy rules) break easily because user queries vary too much</li><li><strong>LLM-based routing</strong> is flexible, but too slow for a system that needs to handle a high load</li></ul><p>What we actually needed was a balance, something as fast as rule-based logic, but flexible enough to understand real-world queries.</p><p>That’s where we landed on a custom machine learning model for domain classification.</p><h3>A Practical ML Breakthrough</h3><p>At this point, the task became pretty concrete: find a model that’s fast enough to handle requests in milliseconds, but still smart enough to understand technical documentation.</p><p>In practice, that usually turns into a trade-off.</p><p>On one side, you have models with more <strong>parameters</strong>, they tend to understand context better. On the other hand, you have model <strong>size</strong>, the smaller it is, the faster it runs and the less pressure it puts on your infrastructure. The goal was to push both in the right direction at once: keep the model lightweight without sacrificing too much quality.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3vaOzmcZyUpefcj3IX-gvA.png" /></figure><p>So how do you end up with something compact, but still capable of handling nuanced queries about APIs and configuration?</p><p>We leaned on two well-known techniques: distillation and quantization.</p><p><strong>Distillation </strong>works like a “teacher–student” setup. A large, pre-trained model (the teacher) already has a strong grasp of context. Instead of using it directly, you train a smaller model (the student) to imitate its behavior. The student doesn’t carry the full weight of the original model, but it retains a surprising amount of its understanding.</p><p><strong>Quantization</strong> tackles a different part of the problem — size. Normally, model parameters are stored in 32-bit precision. By reducing that precision (in our case, down to 8-bit), you significantly shrink the model without dramatically affecting performance. There are even more aggressive approaches, but this turned out to be a good balance for our needs.</p><p>The result is a model that doesn’t quite follow the usual patterns. It delivers a level of understanding you’d expect from something like BERT variants such as <strong>TinyBERT</strong>, while staying much closer in size to lighter models like <strong>BERT_TINY</strong>.</p><p>More importantly, it hits the balance we were after: solid accuracy, low latency, and predictable resource usage, which is exactly what you need when this piece sits in the middle of every request.</p><h3>Wrapping Up</h3><p>In practice, scaling a knowledge system turned out to be less about adding more data and more about structuring it the right way. Some of the seemingly logical decisions, like unifying everything into a single index, introduced more noise than value, especially once real user queries came into play.</p><p>What made the difference was introducing a lightweight machine learning layer for routing. It gave us the responsiveness we needed under load while still handling the nuances of DHTMLX documentation across products.</p><p>If you’re working with AI-assisted development around DHTMLX components, <a href="https://docs.dhtmlx.com/suite/guides/ai/">adding the MCP server</a> becomes more than just a backend piece. It’s what keeps answers grounded in the latest docs and helps avoid those subtle mismatches that tend to slow teams down in real projects.</p><h3>A message from our Founder</h3><p>Hey, <a href="https://linkedin.com/in/sunilsandhu">Sunil</a> here. I wanted to take a moment to thank you for reading until the end and for being a part of this community. Did you know that our team run these publications as a volunteer effort to over 3.5m monthly readers? We don’t receive any funding, we do this to support the community.</p><p>If you want to show some love, please take a moment to follow me on <a href="https://linkedin.com/in/sunilsandhu">LinkedIn</a>, <a href="https://tiktok.com/@messyfounder">TikTok</a>, <a href="https://instagram.com/sunilsandhu">Instagram</a>. You can also subscribe to our <a href="https://newsletter.plainenglish.io/">weekly newsletter</a>. And before you go, don’t forget to clap and follow the writer️!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6d3de12a5901" width="1" height="1" alt=""><hr><p><a href="https://ai.plainenglish.io/scaling-ai-knowledge-systems-lessons-from-the-dhtmlx-mcp-server-6d3de12a5901">Scaling AI Knowledge Systems: Lessons from the DHTMLX MCP Server</a> was originally published in <a href="https://ai.plainenglish.io">Artificial Intelligence in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Simplifying State Management in DHTMLX React Gantt and Scheduler-Powered Apps with Zustand]]></title>
            <link>https://dhtmlx.medium.com/simplifying-state-management-in-dhtmlx-react-gantt-and-scheduler-apps-with-zustand-afea3e329437?source=rss-e9c0a531d5e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/afea3e329437</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[tutorial]]></category>
            <category><![CDATA[gantt-chart]]></category>
            <category><![CDATA[state-management]]></category>
            <dc:creator><![CDATA[JavaScript UI Libraries — DHTMLX]]></dc:creator>
            <pubDate>Fri, 27 Feb 2026 08:48:38 GMT</pubDate>
            <atom:updated>2026-02-27T08:55:41.162Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nJLisspFFeonfwWKORG1sA.png" /></figure><p>Not long ago, we explored how state management affects complex UI components like DHTMLX React Gantt and how <a href="https://dhtmlx.com/blog/using-dhtmlx-gantt-chart-for-react-with-redux-toolkit-principles-benefits-and-implementation-tips/">integrating Redux Toolkit</a> 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.</p><p>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.</p><p>So we decided to continue our series on state management strategies for apps built with <a href="https://dhtmlx.com/docs/products/dhtmlxGantt-for-React/">DHTMLX React Gantt</a> and <a href="https://dhtmlx.com/docs/products/dhtmlxScheduler-for-React/">React Scheduler</a>.</p><p>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.</p><h3>Why Zustand Caught So Much Attention</h3><p>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.</p><p>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.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/694/0*w6RtFzCen7N3KwDF.png" /><figcaption><em>Source: </em><a href="https://risingstars.js.org/2025/en#section-statemanagement"><em>JavaScript Rising Stars 2025</em></a></figcaption></figure><p>So what’s behind that growth?</p><p>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.</p><p>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.</p><p>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.</p><p>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.</p><h3>Zustand vs Redux Toolkit: Where the Difference Shows</h3><p>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.</p><p>But things have been shifting.</p><p>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.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hvjZqGdH-shXDNEdR8DPLg.gif" /><figcaption><em>Source: </em><a href="https://2025.stateofreact.com/en-US/libraries/state-management/"><em>State of React 2025</em></a></figcaption></figure><p>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.</p><p>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.</p><p>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.</p><p>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.</p><p>With that in mind, let’s look at how this plays out when integrating Zustand with DHTMLX React components.</p><p>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.</p><p>If you’d like to try it yourself, you can install the <a href="https://www.npmjs.com/package/@dhtmlx/trial-react-gantt">professional evaluation version</a> directly from npm and experiment in a real project setup.</p><h3>Key Aspects of Integrating DHTMLX React Gantt with Zustand</h3><p>At this point, it’s fair to ask: how do all those advantages of Zustand actually translate into a real React Gantt project?</p><p>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.</p><h4>Defining Store Responsibilities</h4><p>The store plays a bigger role than just holding Gantt data. It also defines how that data changes.</p><p>Here’s the TypeScript structure used in the example:</p><pre>type Snapshot = { tasks: SerializedTask[]; links: Link[]; config: GanttConfig };<br>type State = {<br>  tasks: SerializedTask[];<br>  links: Link[];<br>  config: GanttConfig;<br>  past: Snapshot[];<br>  future: Snapshot[];<br>  maxHistory: number;<br>  recordHistory: () =&gt; void;<br>  undo: () =&gt; void;<br>  redo: () =&gt; void;<br><br>  setZoom: (level: ZoomLevel) =&gt; void;<br>  addTask: (task: SerializedTask) =&gt; SerializedTask;<br>  upsertTask: (task: SerializedTask) =&gt; void;<br>  deleteTask: (id: string | number) =&gt; void;<br>  addLink: (l: Link) =&gt; Link;<br>  upsertLink: (l: Link) =&gt; void;<br>  deleteLink: (id: string | number) =&gt; void;<br>};</pre><p>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.</p><h4>Creating the Store</h4><p>With the structure defined, we can create the Zustand store:</p><pre>export const useGanttStore = create&lt;State&gt;((set, get) =&gt; ({<br>  tasks: seedTasks,<br>  links: seedLinks,<br>  config: { zoom: defaultZoomLevels },<br><br>  past: [],<br>  future: [],<br>  maxHistory: 50,<br>... // actions will go here</pre><p>At this stage, we have an initial state: tasks, links, config, and empty history arrays. The interesting part is how actions modify that state.</p><p>Take Zoom changes as an example:</p><pre>setZoom: (level) =&gt; {<br>  get().recordHistory();<br>  set({<br>    config: { ...get().config, zoom: { ...get().config.zoom, current: level } },<br>  });<br>},</pre><p>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.</p><p>Updating a task follows the same pattern:</p><pre>upsertTask: (task) =&gt; {<br>  get().recordHistory();<br>  const tasks = get().tasks;<br>  const index = tasks.findIndex((x) =&gt; String(x.id) === String(task.id));<br>  if (index !== -1) {<br>    set({<br>      tasks: [...tasks.slice(0, index), { ...tasks[index], ...task }, ...tasks.slice(index + 1)],<br>    });<br>  }<br>},</pre><p>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.</p><p>The same principle applies to links, configuration updates, and undo/redo history. Everything is handled through regular store actions.</p><p>Which leads to the next practical question: how do user interactions inside the Gantt chart trigger these actions?</p><h4>Turning Gantt Events into Store Actions</h4><p>This is where the <strong>data.save</strong> callback comes in. It acts as a bridge between DHTMLX React Gantt and the Zustand store.</p><p>Whenever a user edits a task or link, Gantt triggers <strong>data.save</strong>. That callback determines what kind of operation occurred and routes it to the appropriate store action.</p><pre>const data: ReactGanttProps[&#39;data&#39;] = useMemo(  <br>  () =&gt; ({  <br>    save: (entity, action, item, id) =&gt; {  <br>      if (entity === &#39;task&#39;) {  <br>        const task = item as SerializedTask;  <br>        if (action === &#39;create&#39;) return addTask(task);  <br>        else if (action === &#39;update&#39;) upsertTask(task);  <br>        else if (action === &#39;delete&#39;) deleteTask(id);  <br>      } else if (entity === &#39;link&#39;) {  <br>        const link = item as Link;  <br>        if (action === &#39;create&#39;) return addLink(link);  <br>        else if (action === &#39;update&#39;) upsertLink(link);  <br>        else if (action === &#39;delete&#39;) deleteLink(id);  <br>      }  <br>    },  <br>  }),  <br>  [addTask, addLink, upsertTask, upsertLink, deleteTask, deleteLink]  <br>);</pre><p>In practice, the flow looks like this:</p><ul><li>A user edits a task or dependency in the Gantt UI</li><li>Gantt calls <strong>data.save(…)</strong></li><li>The action type is identified</li><li>The corresponding Zustand action runs</li><li>The store updates</li><li>The chart re-renders with the new state</li></ul><p>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.</p><p>If you want to explore this mechanism in more detail, the “<a href="https://docs.dhtmlx.com/gantt/integrations/react/state/state-management-basics/">Handling changes with data.save</a>” section in the official guide walks through additional examples.</p><h4>Keeping Gantt as a Visual Layer</h4><p>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.</p><p>Here’s how the store is consumed:</p><pre>const { tasks, links, config, setZoom, addTask, upsertTask, deleteTask, addLink, upsertLink, deleteLink, undo, redo } = useGanttStore();</pre><p>And rendering becomes straightforward:</p><pre>&lt;ReactGantt ref={ganttRef} tasks={tasks} links={links} config={config} templates={templates} data={data} /&gt;</pre><p>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.</p><p>If you prefer a full walkthrough, the official documentation includes a <a href="https://docs.dhtmlx.com/gantt/integrations/react/state/zustand/">step-by-step tutorial</a> 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 <a href="https://github.com/dhtmlx/react-gantt-zustand-starter">complete working example</a> available on GitHub for those who want to experiment in a real project setup.</p><h3>Wrapping Up</h3><p>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.</p><p>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.</p><p>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.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=afea3e329437" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[DHTMLX MCP Server: Making AI Coding More Accurate]]></title>
            <link>https://dhtmlx.medium.com/dhtmlx-mcp-server-making-ai-coding-more-accurate-df7c4536b679?source=rss-e9c0a531d5e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/df7c4536b679</guid>
            <category><![CDATA[mcp-server]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[ai-assisted-coding]]></category>
            <dc:creator><![CDATA[JavaScript UI Libraries — DHTMLX]]></dc:creator>
            <pubDate>Fri, 20 Feb 2026 09:17:30 GMT</pubDate>
            <atom:updated>2026-02-21T06:29:09.480Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*x5YPEhf0tC3fuiKXm-J2jw.png" /></figure><p>When a powerful technology arrives, you either move with it or get left behind. Lately, that idea feels more like a day-to-day reality, especially in web development.</p><p>AI tools are showing up everywhere, and ignoring them isn’t really an option anymore. In practice, teams are already adjusting: businesses expect AI-driven features as part of modern products, developers rely on assistants to speed up routine work, and toolmakers are rethinking how their products fit into this new workflow.</p><p>We’ve been going through that same shift. And today, we’re introducing something that came out of it: the <a href="https://dhtmlx.com/docs/products/dhtmlx-mcp-server/">DHTMLX MCP Server</a>. It’s a new addition aimed at making work with DHTMLX documentation noticeably more reliable and efficient when AI is part of your development process.</p><h3>Why MCP Is Becoming Part of the AI Dev Stack</h3><p>These days, tools like Claude Code, Cursor, Gemini, and OpenAI Codex have quietly become part of the everyday dev toolkit. We often see teams relying on them for routine coding, quick debugging, or just navigating documentation faster. But there’s a catch that shows up pretty quickly in practice.</p><p>Most AI assistants are powered by LLMs, and those models don’t continuously learn from new data. They’re trained once and then frozen at a certain point in time, the well-known “knowledge cutoff”. As a result, even strong assistants can fall behind when you’re working with evolving libraries or recently updated APIs. This is exactly the gap that MCP is designed to close.</p><p>At its core, the Model Context Protocol is an open standard that lets AI models connect to external systems and pull in fresh, real-time data. Instead of relying only on what the model “remembers,” it can now access what’s actually current.</p><p>Since its release in November 2024 by Anthropic, MCP has gained traction surprisingly fast. A few signals stand out:</p><ul><li>The ecosystem has grown to more than 10,000 public MCP servers, covering everything from dev tooling to large-scale enterprise use in Fortune 500 environments.</li><li>Major cloud platforms — including Amazon Web Services, Cloudflare, Google Cloud, and Microsoft Azure — are already supporting it at the infrastructure level.</li><li>Official SDKs are available across all major programming languages, with over 97 million monthly downloads.</li></ul><p>Given that momentum, it’s not surprising MCP is quickly becoming one of the defining <a href="https://dhtmlx.com/blog/how-ai-is-reshaping-web-development-in-2026-javascript-frameworks-open-source-and-pm-tools/">patterns for AI development heading into 2026</a>.</p><p>For us, the reasoning was pretty straightforward. If AI tools work better with accurate, up-to-date context, then giving them direct access to DHTMLX documentation is the most reliable way to improve results. That’s what the DHTMLX MCP Server is built for.</p><p>In practice, it means your AI assistant can reference the latest DHTMLX APIs and behavior instead of guessing based on outdated training data.</p><p>So what does that actually change when you’re building with DHTMLX JavaScript components? Let’s take a closer look.</p><h3>How the MCP Server Improves Everyday DHTMLX Workflows</h3><p>If you’ve tried using AI assistants with UI libraries like DHTMLX, you’ve probably seen the rough edges. In practice, it often shows up as slightly off code: outdated APIs, missing config options, or examples that don’t quite match how the library works today.</p><p>These issues are usually tied to what people call AI hallucinations. More often than not, they come from version gaps, when the model simply doesn’t know about recent updates or changes in the library.</p><p>The DHTMLX MCP Server is built to reduce that friction. Instead of relying on stale training data, your AI assistant can work with the latest documentation and produce answers that are much closer to what you’d expect in a real project.</p><p>It integrates with tools many teams are already using, including Claude Code, Cursor, Gemini CLI, and Antigravity. Once connected, a few things noticeably improve:</p><ul><li>responses stay aligned with the latest DHTMLX documentation</li><li>data retrieval is driven by a high-precision embedding model</li><li>generated code snippets reflect current APIs</li><li>suggestions follow established DHTMLX patterns and best practices</li><li>configuration options and customization details are surfaced more consistently</li></ul><p>Another practical detail: the MCP server isn’t limited to a single library. It covers documentation across the entire <a href="https://dhtmlx.com/docs/products/">DHTMLX JavaScript product line</a>. Whether you’re building a project management tool with DHTMLX Gantt or DHTMLX Scheduler, working with diagrams via DHTMLX Diagram, or assembling dashboards with DHTMLX Suite, the assistant can pull in the right context when you need it.</p><p>Security is another area teams usually ask about. The MCP server is a hosted service and doesn’t require access to your local files. It also doesn’t collect personal user data, which makes it easier to adopt in environments with stricter requirements.</p><p>If you want to try it out, the <a href="https://docs.dhtmlx.com/suite/guides/ai/">official starter guide</a> walks through the setup process and shows how to connect the MCP server to your AI assistant. It’s a straightforward way to see how much more reliable AI-assisted development can feel when the underlying context is actually up to date.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=df7c4536b679" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building High-Performance Project Management Apps with DHTMLX React Gantt and Next.js]]></title>
            <link>https://dhtmlx.medium.com/building-high-performance-project-management-apps-with-dhtmlx-react-gantt-and-next-js-d84de6364627?source=rss-e9c0a531d5e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/d84de6364627</guid>
            <category><![CDATA[project-management]]></category>
            <category><![CDATA[web-app-development]]></category>
            <category><![CDATA[nextjs]]></category>
            <category><![CDATA[gantt-chart]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[JavaScript UI Libraries — DHTMLX]]></dc:creator>
            <pubDate>Mon, 26 Jan 2026 13:28:07 GMT</pubDate>
            <atom:updated>2026-01-26T13:28:07.638Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2C3ZE9OTRe1BgS7k7Lt_-g.png" /></figure><p>Developing web-based project management applications in React can feel deceptively simple until your app needs to handle thousands of tasks, complex dependencies, and live scheduling updates. Developers quickly run into performance bottlenecks: slow initial page loads, complicated state management, and difficulty scaling components efficiently.</p><p>This is where combining Next.js and <a href="https://dhtmlx.com/docs/products/dhtmlxGantt-for-React/">DHTMLX Gantt for React</a> becomes a game-changer. Together, they let teams build responsive, data-intensive project planning tools without reinventing the wheel.</p><p>In this guide, we’ll walk through why Next.js is ideal for React projects, how DHTMLX Gantt leverages its strengths, and practical steps to integrate them into a scalable, production-ready application.</p><h4>Why React Alone Isn’t Enough for Complex Project Tools</h4><p>React excels at building interactive UIs, but as soon as a Gantt chart needs to display thousands of interdependent tasks, the framework alone doesn’t address core challenges:</p><ul><li>Rendering delays when data sets are large</li><li>Client-side performance bottlenecks</li><li>SEO limitations in fully dynamic pages</li><li>Complex routing and server interactions</li></ul><p>Teams often patch these issues with custom tooling, a time-consuming approach that increases maintenance costs. The solution? Next.js, which provides built-in optimizations for these exact pain points.</p><h4>Next.js: Powerful Performance Layer for React</h4><p>Rather than simply explaining features, let’s look at a real-world scenario: imagine your Gantt chart displays 5,000 tasks with dependencies and resource assignments. Without server-side assistance, the initial page load can take seconds, frustrating users.</p><p>Next.js solves this by enabling:</p><ul><li>Server-side rendering (SSR) for pages that need immediate content</li><li>Static site generation (SSG) for task templates or dashboards that rarely change</li><li>Hybrid rendering to mix static and dynamic pages efficiently</li></ul><p>Additional tools — automatic code splitting, image optimization, data prefetching, and caching mechanisms — reduce runtime overhead. Built-in routing and modern dev tooling like Turbopack streamline development, letting teams focus on product logic rather than boilerplate performance hacks.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/827/1*t4PlCKjh7OyLY_L7z8S94Q.png" /><figcaption><a href="https://survey.stackoverflow.co/2025/technology#most-popular-technologies-webframe-prof"><em>2025 Stack Overflow Developer Survey</em></a></figcaption></figure><p>Over time, Next.js has shifted from an optional enhancement to the default architecture choice for React teams building enterprise applications. Industry surveys, including Capterra’s latest Developer Survey, confirm its widespread adoption alongside React.</p><h3>Why DHTMLX Gantt Shines in This Environment</h3><p>While Next.js handles architecture and performance, DHTMLX Gantt for React tackles the complexity of project scheduling itself. Its strengths include:</p><ul><li>Smart rendering for thousands of tasks</li><li>Dynamic loading to keep the interface responsive</li><li>Advanced scheduling features: auto-scheduling, resource assignments, critical path calculation, task grouping and splitting</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BypQ7HH8K-hKZUkFPjifHQ.png" /><figcaption><a href="https://dhtmlx.com/react/demos/gantt/#/resource-panel"><em>React Gantt chart with resource histogram</em></a></figcaption></figure><p>In practice, this allows project managers to monitor large initiatives efficiently. <a href="https://dhtmlx.com/blog/using-dhtmlx-gantt-chart-for-react-with-redux-toolkit-principles-benefits-and-implementation-tips/">Combined with Redux Toolkit for state management</a>, it solves the typical performance and complexity challenges of client-side Gantt components.</p><p>Importantly, DHTMLX React Gantt integrates seamlessly with Next.js, so you don’t need hacks or workarounds, everything works in a standard React + Next.js setup.</p><h3>Step-by-Step Integration with Next.js</h3><p>Getting started with DHTMLX React Gantt in a Next.js project is straightforward, even for developers with basic React experience:</p><ol><li>Install Node.js if not already installed</li><li>Create a new Next.js project using <strong>npx create-next-app@latest</strong></li><li>Install the DHTMLX React Gantt package (<a href="https://docs.dhtmlx.com/gantt/integrations/react/installation/">guide</a>)</li><li>Prepare demo data for tasks and links</li><li>Create a Gantt component and embed it in a page</li><li>Run your app to confirm functionality</li></ol><p><strong>Pro tip:</strong> Because Gantt relies on refs, hooks, and callbacks, it must be rendered inside a Client Component. Next.js defaults to Server Components, which lack access to browser APIs. The <a href="https://docs.dhtmlx.com/gantt/integrations/react/nextjs/">official DHTMLX Gantt tutorial explains</a> this in detail with examples.</p><p>For convenience, a <a href="https://github.com/dhtmlx/react-gantt-nextjs-starter">starter app on GitHub</a> demonstrates a fully working Next.js + DHTMLX React Gantt integration.</p><h3>Real-World Benefits</h3><p>By combining Next.js and DHTMLX Gantt, teams can:</p><ul><li>Deliver highly interactive, responsive dashboards</li><li>Handle large-scale scheduling data without slowing down the app</li><li>Build a scalable architecture that grows with project complexity</li><li>Move quickly from prototype to production, thanks to predictable performance</li></ul><p>This pairing allows developers to focus on user experience and business logic, instead of fighting framework limitations.</p><h3>Conclusion</h3><p>Creating modern, enterprise-grade project management apps requires predictable architecture, rich UI components, and strong performance. React alone is not enough to manage the demands of data-heavy Gantt charts and complex workflows.</p><p>Next.js provides the architectural backbone, while DHTMLX React Gantt delivers the scheduling and visualization features necessary for professional project management tools. Together, they enable teams to build scalable, high-performance applications that work well for both developers and end users.</p><p>Following the official integration guide, teams can quickly move from small prototypes to production-ready project management solutions that handle real-world complexities with confidence.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d84de6364627" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Streamlining State Management in DHTMLX Gantt for React with Redux Toolkit]]></title>
            <link>https://dhtmlx.medium.com/streamlining-state-management-in-dhtmlx-gantt-for-react-with-redux-toolkit-a7c0e9d57651?source=rss-e9c0a531d5e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/a7c0e9d57651</guid>
            <category><![CDATA[redux-toolkit]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[gantt-chart]]></category>
            <category><![CDATA[state-management]]></category>
            <dc:creator><![CDATA[JavaScript UI Libraries — DHTMLX]]></dc:creator>
            <pubDate>Mon, 05 Jan 2026 11:23:14 GMT</pubDate>
            <atom:updated>2026-01-05T11:23:14.918Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NJ045gdbrFlZl6hCLrXxfA.png" /></figure><p>Modern React-based project management applications rely heavily on powerful UI components to support rich user interactions. However, the more functionality these components provide, the harder it becomes to consistently synchronize the interface with frequent user-driven changes and data updates. This issue is especially noticeable in Gantt charts, where moving tasks, modifying links, or changing the timeline can immediately impact multiple parts of the application state. In this context, a well-organized state management approach is critical to maintaining a smooth user experience.</p><p>In this article, we’ll explain why DHTMLX Gantt for React pairs effectively with Redux Toolkit when building data-centric Gantt applications, and we’ll share practical recommendations for integrating this setup into your own project.</p><h3><strong>How DHTMLX Gantt for React Fits Redux Toolkit’s Data Flow</strong></h3><p>The DHTMLX <a href="https://dhtmlx.com/docs/products/dhtmlxGantt-for-React/">Gantt component for React</a> is designed to handle complex project scheduling scenarios. It supports everything from basic data operations (creating, editing, and removing tasks or links) and global configuration options (zoom levels, filters, themes) to advanced scheduling features such as critical path calculation, auto-scheduling, and resource management. All of these capabilities are exposed through the API of the DHTMLX <a href="https://dhtmlx.com/docs/products/dhtmlxGantt/">JavaScript Gantt</a>, making them accessible during integration into a React application.</p><p>In real-world Gantt charts, tasks rarely exist in isolation. A single interaction, like dragging a task or updating a dependency, can cascade into multiple changes across the project timeline. Managing these interconnected updates consistently and predictably requires more than local component state. This is where a dedicated state management solution such as Redux Toolkit becomes especially valuable.</p><p>The React version of DHTMLX Gantt satisfies two key conditions that allow it to integrate seamlessly with Redux Toolkit:</p><ul><li><strong>Declarative data handling</strong></li></ul><p>The component follows React’s declarative paradigm by receiving all core data — including tasks, links, resources, and configuration options like columns, templates, and themes — through props. When Redux is used, these props can be sourced directly from the centralized store. Any state change in Redux is automatically reflected in the Gantt chart, ensuring that the UI always stays in sync with the application state.</p><ul><li><strong>Event-driven callbacks for user interactions</strong></li></ul><p>To propagate user-driven changes back to the store, the Gantt component exposes a set of callback functions that report interaction details. Instead of mutating its internal state, the component notifies the application about updates, allowing Redux Toolkit to process and apply them centrally. This approach reinforces Redux as the single source of truth and keeps state mutations explicit and traceable.</p><p>Since Redux Toolkit is built with TypeScript in mind, it’s also worth mentioning that DHTMLX Gantt for React provides full TypeScript support. While optional, strong typing helps reduce integration friction and improves reliability across the entire data flow.</p><p>With this foundation in place, let’s take a closer look at why Redux Toolkit is particularly well-suited for React applications built around Gantt charts.</p><h3>Why Redux Toolkit Is a Strong Choice for React Gantt Applications</h3><p>Classic Redux is often perceived as verbose and difficult to configure, especially in projects that require multiple reducers, middleware, and third-party helpers. <strong>Redux Toolkit</strong> was introduced to simplify this experience by reducing boilerplate and providing sensible defaults. For complex interactive components like a React-based Gantt chart, this streamlined approach makes Redux Toolkit a practical and developer-friendly option.</p><h4>How the Integration Works in Real Projects</h4><p>When Redux Toolkit is used, all Gantt-related data and configuration are stored in a single centralized state container, divided into logical units known as <em>slices</em>. Each slice represents a specific aspect of the Gantt chart, such as tasks, links, or UI settings.</p><p>When a user interacts with the chart — for example, by creating or editing a task — the Gantt component triggers a callback that dispatches an action describing the change. Redux Toolkit processes this action through the corresponding reducer, updates the state accordingly, and then supplies the updated data back to the Gantt component via props. React automatically re-renders the UI to reflect the new state.</p><p>For a deeper dive into this mechanism, the official <a href="https://redux-toolkit.js.org/tutorials/quick-start">Redux Toolkit documentation</a> provides a detailed explanation of the data flow.</p><p><strong>Key Advantages of Using Redux Toolkit with UI Components:</strong></p><ul><li><strong>Single source of truth<br></strong> All Gantt data and configuration settings are stored in one Redux store, ensuring consistent state across the entire application and eliminating synchronization issues.</li><li><strong>Transparent and predictable updates<br></strong> Redux Toolkit enforces a one-way data flow, making every state transition explicit. This greatly simplifies debugging, especially when multiple interactions affect the same data over time.</li><li><strong>Well-defined responsibilities<br></strong> State management logic lives in Redux Toolkit, while the DHTMLX Gantt component focuses solely on rendering and capturing user input. This separation keeps the codebase cleaner and easier to reason about.</li><li><strong>Scalable architecture<br></strong> By organizing the state into feature-based slices, Redux Toolkit allows the Gantt application to grow without becoming difficult to maintain. New features can be added incrementally while preserving a clear and structured codebase.</li></ul><p>Understanding why DHTMLX Gantt for React and Redux Toolkit work well together is only part of the story. Next, we’ll move from theory to practice and explore how this integration is implemented step by step.</p><h3><strong>Key Integration Steps for Connecting DHTMLX React Gantt with Redux Toolkit</strong></h3><p>At this stage, the foundational concepts behind this integration (callbacks, slices, actions, and reducers) should already be familiar. Now it’s time to look at how these elements interact in practice to provide reliable and predictable state management for a React-based Gantt chart. Specifically, we’ll focus on how the Redux infrastructure is assembled (store and slice) and how state updates from different sources are processed consistently.</p><h4>Building the Redux State Layer</h4><p>From the Redux perspective, two building blocks define the state management architecture for a DHTMLX Gantt project: the Redux store and the Gantt slice. The store acts as the central repository for all Gantt-related data, while slices encapsulate the logic responsible for modifying that data in response to user interactions and application events.</p><p>Together, these elements form a scalable and maintainable foundation for managing Gantt state in React applications.</p><p>The first implementation step is creating a centralized Redux store and registering the reducer responsible for handling Gantt-related state. While traditional Redux setups often involve extensive configuration, Redux Toolkit simplifies this process through the <strong>configureStore</strong> API:</p><pre>import { configureStore } from &#39;@reduxjs/toolkit&#39;;  <br>import ganttReducer from &#39;./ganttSlice&#39;;<br><br>export const store = configureStore({  <br>  reducer: {  <br>    gantt: ganttReducer,  <br>  },  <br>});<br><br>export type RootState = ReturnType&lt;typeof store.getState&gt;;  <br>export type AppDispatch = typeof store.dispatch;</pre><p>The <strong>RootState</strong> and <strong>AppDispatch</strong> types are inferred directly from the store instance. These types are later used alongside <strong>useSelector</strong> and <strong>useDispatch</strong> hooks to ensure type-safe access to Gantt data and action dispatching within the UI layer.</p><h4>Defining the Redux Gantt Slice</h4><p>Next, we describe how Gantt data is stored and updated by implementing a dedicated Redux slice. In this example, the slice manages tasks, links, configuration options such as zoom levels, and undo/redo functionality. Redux Toolkit’s <strong>createSlice</strong> function is used to define the slice name, initial state, and reducer logic in a concise format. For each reducer, the corresponding action creator is generated automatically.</p><pre>const ganttSlice = createSlice({<br>  name: &#39;gantt&#39;,<br>  initialState,<br>  reducers: {<br>    undo(state) {<br>        ...<br>    },<br>    redo(state) {<br>      ...<br>    },<br>    updateTask(state, action: PayloadAction&lt;SerializedTask&gt;) {<br>      ...<br>    },<br>    createTask(state, action: PayloadAction&lt;SerializedTask&gt;) {<br>      ...<br>    },<br>    deleteTask(state, action: PayloadAction&lt;string&gt;) {<br>      ...<br>    },<br>    updateLink(state, action: PayloadAction&lt;Link&gt;) {<br>      ...<br>    },<br>    createLink(state, action: PayloadAction&lt;Link&gt;) {<br>      ...<br>    },<br>    deleteLink(state, action: PayloadAction&lt;string&gt;) {<br>      ...<br>    },<br>    setZoom(state, action: PayloadAction&lt;ZoomLevel&gt;) {<br>      ...<br>    },<br>  },<br>});</pre><p>The full implementation of the Gantt slice can be found in the <a href="https://docs.dhtmlx.com/gantt/integrations/react/state/redux-toolkit/">complete DHTMLX Gantt tutorial</a> available in the official documentation.</p><p><strong>Key Takeaways from This Setup</strong></p><ul><li><strong>Initial state definition</strong><br>The <strong>initialState</strong> represents the full Gantt data structure stored in Redux and serves as the baseline for all subsequent updates.</li><li><strong>Reducer-driven updates<br></strong>Each reducer specifies how the state changes in response to a particular action, including task manipulation, link updates, zoom adjustments, and navigation through state history.</li><li><strong>Auto-generated actions</strong><br>Action creators produced by <strong>createSlice</strong> are dispatched from the UI layer or external controls such as toolbars (covered later).</li><li><strong>Undo/redo support<br></strong>State history management is implemented using snapshot-based rollback, enabling straightforward undo and redo functionality.</li></ul><p>At this point, the Redux store and Gantt slice together provide a clear, structured mechanism for managing Gantt state. With this foundation in place, the application is ready to handle state updates from both internal Gantt interactions and external UI controls in a consistent way.</p><h3>Handling Gantt State Updates with Redux Toolkit</h3><p>Now we reach one of the most practical aspects of the integration: how <strong>Redux Toolkit</strong> manages state changes coming from different sources. In a React Gantt application, updates can originate either from the Gantt component itself or from external UI elements such as toolbars and controls. Redux Toolkit handles both scenarios using the same transparent and predictable mechanism built around callbacks and props.</p><p>Internal updates and external commands follow a single data flow, making all state transitions easy to track and debug.</p><h4>Processing Internal Changes from the Gantt Chart</h4><p>Whenever a user edits a task, creates a dependency, or removes an item directly in the Gantt chart, the application needs to capture this event and reflect it in the global state. This communication happens through the <strong>data.save</strong> callback exposed by the DHTMLX Gantt component.</p><pre>const data: ReactGanttProps[&#39;data&#39;] = useMemo(<br> () =&gt; ({<br>   save: (entity, action, payload, id) =&gt; {<br>     if (entity === &#39;task&#39;) {<br>       const task = payload as SerializedTask;<br>       if (action === &#39;update&#39;) {<br>         dispatch(updateTask(task));<br>       } else if (action === &#39;create&#39;) {<br>         dispatch(createTask(task));<br>       } else if (action === &#39;delete&#39;) {<br>         dispatch(deleteTask(String(id)));<br>       }<br>     } else if (entity === &#39;link&#39;) {<br>     ... //similar logic for links<br>     }<br>   },<br> }),<br> [dispatch]<br>);</pre><p>The <strong>data.save</strong> callback supplies detailed context about each modification, including:</p><ul><li><strong>entity</strong>–the type of element being changed (task or link)</li><li><strong>action</strong>–the operation performed (create, update, or delete)</li><li><strong>payload</strong>–the data associated with the change</li><li><strong>id</strong> – the unique identifier of the affected item</li></ul><p>Based on the received parameters, the callback dispatches the appropriate Redux action. This approach ensures that all internal Gantt updates are immediately reflected in the centralized Redux store, without relying on local component state.</p><p>For a more detailed explanation of the <strong>data.save</strong> mechanism, refer to the corresponding <a href="https://docs.dhtmlx.com/gantt/integrations/react/state/state-management-basics/#handlingchangeswithdatasave">section</a> in the official documentation.</p><h4>Syncing the Gantt UI with Redux State</h4><p>Once Redux processes an update, the Gantt component receives the latest state through props. The required pieces of data (tasks, links, and configuration) are extracted from the store using the <strong>useSelector</strong> hook:</p><pre>const { tasks, links, config } = useSelector((state: RootState) =&gt; state.gantt);</pre><p>Any change in the Redux state automatically triggers a re-render, ensuring that the Gantt chart always reflects the current application state without manual synchronization.</p><p>With this setup, state updates in a React Gantt application follow a clear and consistent sequence:</p><p><strong>Gantt interaction → Redux action → reducer execution → store update → Gantt receives new state → UI re-render</strong></p><p>This predictable cycle allows both internal chart interactions and external UI controls to coexist within the same state management model, providing stability and clarity as the application grows.</p><h4>Controlling the Gantt Chart through an External Toolbar</h4><p>One of the key advantages of integrating DHTMLX Gantt with Redux<strong> Toolkit</strong> is the ability to manipulate the chart state from external UI elements. This allows developers to build flexible, feature-rich React project management interfaces while maintaining a single, predictable data flow. Let’s see how this works using a custom toolbar with undo, redo, and zoom capabilities.</p><h4>Toolbar Setup and Callbacks</h4><p>The toolbar receives the current zoom level as a prop and exposes callback functions for user actions:</p><pre>export interface ToolbarProps {  <br>  onUndo?: () =&gt; void;  <br>  onRedo?: () =&gt; void;  <br>  onZoom?: (level: ZoomLevel) =&gt; void;  <br>  currentZoom?: ZoomLevel;  <br>}</pre><p>Whenever a user clicks a toolbar button, the corresponding callback is invoked:</p><pre>&lt;Button onClick={() =&gt; onUndo?.()}&gt;<br>  &lt;UndoIcon /&gt;<br>&lt;/Button&gt;<br><br>&lt;Button onClick={() =&gt; onRedo?.()}&gt;<br>  &lt;RedoIcon /&gt;<br>&lt;/Button&gt;<br>// Additional buttons as needed</pre><p><strong>Connecting Toolbar Actions to Redux</strong></p><p>These callback functions are linked to Redux actions, ensuring that the toolbar can update the centralized Gantt state:</p><pre>const handleUndo = useCallback(() =&gt; {  <br>  dispatch(undo());  <br>}, [dispatch]);<br><br>const handleRedo = useCallback(() =&gt; {  <br>  dispatch(redo());  <br>}, [dispatch]);<br><br>const handleZoomIn = useCallback(  <br>  (newZoom: ZoomLevel) =&gt; {  <br>    dispatch(setZoom(newZoom));  <br>  },  <br>  [dispatch]  <br>);</pre><p>Using <strong>useCallback</strong> for dispatch handlers helps prevent unnecessary re-renders. When an action is dispatched, Redux updates the corresponding slice, keeping the Gantt state in sync with toolbar interactions.</p><p>Finally, the Gantt chart is rendered alongside the toolbar:</p><pre>return (<br>  &lt;div style={{ height: &#39;100%&#39;, display: &#39;flex&#39;, flexDirection: &#39;column&#39; }}&gt;<br>    &lt;Toolbar<br>      onUndo={handleUndo}<br>      onRedo={handleRedo}<br>      onZoom={handleZoomIn}<br>      currentZoom={config.zoom.current}<br>    /&gt;<br><br>    &lt;ReactGantt<br>      tasks={tasks}<br>      links={links}<br>      config={ganttConfig}<br>      templates={templates}<br>      data={data}<br>      ref={ganttRef}<br>    /&gt;<br>  &lt;/div&gt;<br>);</pre><p>This setup ensures that all state changes, whether triggered by direct chart interactions or toolbar buttons, flow through the same Redux-controlled cycle, maintaining consistency and predictability.</p><p>For a complete step-by-step guide, check the <a href="https://docs.dhtmlx.com/gantt/integrations/react/state/redux-toolkit/">official DHTMLX Gantt documentation</a> or explore the <a href="https://github.com/dhtmlx/react-gantt-redux-starter">working example on GitHub</a>.</p><h3>Wrapping-Up</h3><p>Combining DHTMLX Gantt for React with Redux Toolkit creates a clear and reliable state management framework for handling complex project data. Whether changes originate from direct interactions within the Gantt chart or through external toolbar controls, Redux ensures every update is processed consistently and reflected immediately in the UI. This integration allows development teams to build robust React-based project management applications while keeping the user interface decoupled from business logic, promoting maintainability and scalability.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a7c0e9d57651" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Add AI-Driven Styling to DHTMLX Gantt]]></title>
            <link>https://ai.plainenglish.io/how-to-add-ai-driven-styling-to-dhtmlx-gantt-8fa713672627?source=rss-e9c0a531d5e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/8fa713672627</guid>
            <category><![CDATA[gantt-chart]]></category>
            <category><![CDATA[project-management]]></category>
            <category><![CDATA[styling]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[ai]]></category>
            <dc:creator><![CDATA[JavaScript UI Libraries — DHTMLX]]></dc:creator>
            <pubDate>Tue, 16 Dec 2025 09:32:14 GMT</pubDate>
            <atom:updated>2025-12-19T17:17:21.085Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0BdYYig9pHXjd8-uDk4ivw.png" /></figure><p>As AI becomes a natural part of modern development workflows, teams are increasingly looking for practical integration patterns, not just generic chatbots. One common challenge is converting free-text user requests into safe, predictable configuration changes for complex UI components. DHTMLX Gantt illustrates this problem well: it exposes a rich set of configuration options and theme variables, but experimenting with different layouts or visual styles can be time-consuming when done manually.</p><p>A practical solution is to introduce a thin AI layer that accepts targeted instructions in plain English and translates them into structured configuration updates. The key principle is to keep the configurable surface intentionally small and well-defined, while letting the AI model handle the mapping from natural language to JSON. In this tutorial, you’ll learn how to add such a smart design assistant to an existing DHTMLX-based JavaScript Gantt chart — without rebuilding or refactoring the entire UI.</p><h3>Demo Overview: Architecture and Interaction Model</h3><p>This guide is based on the official AI Gantt Theme Builder demo created by the DHTMLX team.</p><p>The demo application uses a simple, purpose-driven layout:</p><ul><li>A Gantt chart with project data displayed on the left</li><li>An assistant panel on the right, featuring <strong>Chat</strong> and <strong>Code</strong> tabs</li></ul><p>This structure encourages experimentation. Instead of editing stylesheets or configuration files directly, users can describe the desired visual changes in plain English and immediately see the results applied to the chart.</p><p>Typical requests might include:</p><ul><li>Applying a warmer color palette</li><li>Increasing row or task height</li><li>Making dependency links more visually prominent</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sXZ_nDfFIZ3HDyhoSaEO_g.gif" /><figcaption><a href="https://dhtmlx.com/docs/demo/ai-gantt-theme-builder/">Open live demo</a></figcaption></figure><p>The complete demo source code and technology stack details are available in the <a href="https://github.com/DHTMLX/gantt-theme-builder-ai-demo">official GitHub repository</a>.</p><h3>End-to-End Flow: From User Intent to UI Update</h3><p>Before diving into implementation, it helps to understand how the system processes a request:</p><ol><li>A user describes a desired visual or layout change in the Chat tab</li><li>The frontend forwards the request, along with the current Gantt state, to the backend</li><li>The backend contextualizes and constrains the request before sending it to the LLM</li><li>The AI model produces a structured JSON response using predefined tool functions</li><li>The frontend applies the resulting configuration and styling updates in real time</li></ol><p>What makes this flow reliable is the transformation of free-form language into deterministic, executable instructions. This conversion is handled using the OpenAI API (or a compatible LLM) via the function-calling (<a href="https://platform.openai.com/docs/guides/function-calling">tool-calling</a>) mechanism.</p><p>With that foundation in place, let’s walk through the implementation step by step.</p><h3>Designing a Frontend Ready for AI Interaction</h3><p>The frontend’s role in this system is deliberately focused. It does not interpret user intent or make decisions — it simply relays intent, applies validated updates, and reflects changes visually.</p><h4>Initializing the Gantt Chart and Styling Model</h4><p>The first step is initializing the <a href="https://dhtmlx.com/docs/products/dhtmlxGantt/">DHTMLX Gantt component</a> and configuring its columns, plugins, and helpers in the <a href="https://github.com/DHTMLX/gantt-theme-builder-ai-demo/blob/main/frontend/src/main.js">main.js</a> file:</p><pre>const gantt = Gantt.getGanttInstance();<br>gantt.config.columns = [<br>  { name: &#39;wbs&#39;, label: &#39;WBS&#39;, width: 60, resize: true, template: gantt.getWBSCode },<br>  { name: &#39;text&#39;, label: &#39;Task name&#39;, tree: true, width: 250, resize: true },<br>  { name: &#39;start_date&#39;, align: &#39;center&#39;, width: 100, resize: true },<br>  { name: &#39;duration&#39;, align: &#39;center&#39;, width: 80, resize: true },<br>  { name: &#39;add&#39;, width: 40 },<br>];<br>gantt.plugins({<br>  auto_scheduling: true,<br>  undo: true,<br>  export_api: true,<br>  marker: true,<br>  tooltip: true,<br>});<br><br>gantt.config.auto_scheduling = true;<br>gantt.config.open_tree_initially = true;<br>gantt.config.auto_types = true;<br>gantt.config.scale_height = 60;<br>gantt.config.link_radius = 4;<br>initZoom(gantt);<br>fitTaskText(gantt);</pre><p>DHTMLX Gantt styling is controlled through two complementary layers:</p><ul><li>CSS variables, which define colors, fonts, and visual accents</li><li>The <strong>gantt.config</strong> object, which controls structural properties such as row height, task height, and link width</li></ul><p>Because both layers support runtime updates, AI-generated changes can be applied instantly, without reloading the page or reinitializing the component.</p><p>To support incremental changes, the demo keeps track of the current theme and configuration state. New AI-generated values are merged into the existing setup rather than replacing it entirely, ensuring that previous adjustments are preserved.</p><p><strong><em>Note:</em></strong><em> The demo currently uses temporary message storage. This setup anticipates future support for full conversation history, including user prompts, AI responses, and executed tool calls.</em></p><h4>Assistant Interface: Chat and Code tabs</h4><p>The AI assistant panel is split into two tabs with clearly separated responsibilities.</p><p>The Chat tab handles natural-language interaction. It is built on a lightweight, embeddable chat widget that sends user prompts to the backend and displays AI responses.</p><pre>...<br>  // Initialize the chat widget with dependencies<br>export const initChat = ({ socket, runCommand, getTheme, getConfigs }) =&gt; {<br> <br>  // Create and set up the chat UI<br>    const chatWidgetContainer = document.querySelector(&#39;#chat-tab&#39;);<br>    chatWidgetContainer.dataset.id = &#39;0&#39;;<br><br>    chatWidgetContainer.innerHTML = `<br>      &lt;div id=&quot;chat-popup&quot; class=&quot;relative left-0 bottom-0 h-full w-full bg-white rounded-md shadow-md flex flex-col transition-all&quot;&gt;<br>        &lt;div id=&quot;chat-header&quot; class=&quot;flex justify-between items-center p-4 bg-gray-800 text-white rounded-t-md&quot;&gt;<br>          &lt;h3 class=&quot;m-0 text-lg&quot;&gt;DHX Assistant&lt;/h3&gt;<br>           &lt;button data-micromodal-trigger=&quot;modal-1&quot; class=&quot;help-btn&quot;&gt;?&lt;/button&gt;<br>        &lt;/div&gt;<br>        &lt;div id=&quot;chat-messages&quot; class=&quot;flex-1 p-4 overflow-y-auto text-base&quot;&gt;&lt;/div&gt;<br>        &lt;div id=&quot;loader&quot; class=&quot;hidden justify-start mb-3&quot;&gt;<br>          &lt;div class=&quot;spinner m-auto&quot;&gt;&lt;/div&gt;<br>        &lt;/div&gt;<br>        &lt;div id=&quot;chat-input-container&quot; class=&quot;p-4 border-t border-gray-200&quot;&gt;<br>          &lt;div class=&quot;flex space-x-4 items-center&quot;&gt;<br>            &lt;input type=&quot;text&quot; id=&quot;chat-input&quot; class=&quot;flex-1 border border-gray-300 rounded-md px-4 py-2 outline-none w-3/4&quot; placeholder=&quot;Type your message...&quot;&gt;<br>            &lt;button id=&quot;chat-submit&quot; class=&quot;bg-gray-800 text-white rounded-md px-4 py-2 cursor-pointer&quot;&gt;Send&lt;/button&gt;<br>          &lt;/div&gt;<br>        &lt;/div&gt;<br>      &lt;/div&gt;<br>   `;<br>...</pre><p>The widget configuration can be found in the corresponding <a href="https://github.com/DHTMLX/gantt-theme-builder-ai-demo/blob/main/frontend/src/chat-widget.js">demo file</a>. In addition to basic messaging, it can suggest follow-up actions and guide users toward more precise requests.</p><p>The Code tab becomes relevant after changes are applied. It allows users to inspect, and optionally refine, the generated CSS variables and configuration values using dedicated editors managed by the <a href="https://github.com/DHTMLX/gantt-theme-builder-ai-demo/blob/main/frontend/src/theme-manager.js"><strong>ThemeManager</strong></a><strong>.</strong></p><pre>initEditors() {<br>    this.initCssEditor();<br>    this.initConfigEditor();<br>  }<br><br>  initCssEditor() {<br>    require.config({ paths: { vs: &quot;https://cdn.jsdelivr.net/npm/monaco-editor@0.34.1/min/vs&quot; } });<br>    require([&quot;vs/editor/editor.main&quot;], () =&gt; {<br>      this.cssEditor = monaco.editor.create(document.getElementById(&quot;css-editor-container&quot;), {<br>        value: this.originalThemeCSS,<br>        language: &quot;css&quot;,<br>        theme: &quot;vs-dark&quot;,<br>        automaticLayout: true,<br>        readOnly: false,<br>      });<br><br><br>      this.setupEditorEvents(this.cssEditor, &quot;css&quot;);<br>    });<br>  }<br><br>  initConfigEditor() {<br>    require.config({ paths: { vs: &quot;https://cdn.jsdelivr.net/npm/monaco-editor@0.34.1/min/vs&quot; } });<br>    require([&quot;vs/editor/editor.main&quot;], () =&gt; {<br>      this.configEditor = monaco.editor.create(document.getElementById(&quot;config-editor-container&quot;), {<br>        value: this.originalThemeConfigs,<br>        language: &quot;javascript&quot;,<br>        theme: &quot;vs-dark&quot;,<br>        automaticLayout: true,<br>        readOnly: false,<br>      });<br><br>      this.setupEditorEvents(this.configEditor, &quot;config&quot;);<br>    });<br>  }</pre><p>The <strong>ThemeManager</strong> stores both the initial and modified code and applies updates in real time.</p><pre>setupEditorEvents(editor, type) {<br>  editor.onDidChangeModelContent(() =&gt; {<br>    const value = editor.getValue();<br>    this[`editedTheme${type === &quot;css&quot; ? &quot;CSS&quot; : &quot;Configs&quot;}`] = value;<br><br><br>    this.updateButtonsState(type);<br>    this.applyThemeOnFly();<br>  });<br><br>  document.querySelector(`[data-editor=&quot;${type}&quot;].copy-editor-btn`).addEventListener(&quot;click&quot;, () =&gt; {<br>    this.copyContent(type);<br>  });<br><br>  document.querySelector(`[data-editor=&quot;${type}&quot;].save-editor-btn`).addEventListener(&quot;click&quot;, () =&gt; {<br>    this.saveChanges(type);<br>  });<br><br><br>  document.querySelector(`[data-editor=&quot;${type}&quot;].cancel-editor-btn`).addEventListener(&quot;click&quot;, () =&gt; {<br>    this.cancelChanges(type);<br>  });<br> }</pre><p>It also supports <strong>Save</strong>, <strong>Cancel</strong>, and <strong>Copy</strong> actions. When changes are confirmed, they are routed back into the application through the same command execution mechanism used by the AI.</p><h4>Forwarding User Requests to the Backend</h4><p>When a user submits a prompt, the frontend sends it to the backend via a WebSocket connection established with Socket.IO:</p><pre>const SOCKET_URL = import.meta.env.VITE_SOCKET_URL || `${window.location.origin}`;<br>const socket = io(SOCKET_URL);</pre><p>Along with the prompt, the frontend includes the current Gantt theme and configuration state:</p><pre>function sendUserMessage(message) {<br>  displayUser(message);<br>   <br>  const payload = {<br>    message,<br>    theme: getTheme(), // sends current theme state<br>    configs: getConfigs(), // sends current config state<br>    };<br>    socket.emit(&#39;user_msg&#39;, JSON.stringify(payload)); // sends the payload to the backend via WebSocket<br>}</pre><p>From this point onward, the backend assumes responsibility for interpretation, validation, and AI interaction.</p><h3>Backend Logic: Constraining and Interpreting AI Commands</h3><p>The backend acts as the system’s trust boundary. Its primary responsibility is to ensure that AI-generated output remains safe, predictable, and limited to approved configuration surfaces.</p><h3>Defining Allowed Styling Operations</h3><p>Allowing an AI model to generate arbitrary code would be unsafe. Instead, the backend exposes a controlled set of operations, defined as tool functions, using a strict JSON schema.</p><p>In this demo, two primary functions are available:</p><ul><li><strong>set_theme</strong> — applies updates to CSS variables and Gantt configuration option</li></ul><pre>{<br>  name: &quot;set_theme&quot;,<br>  parameters: {<br>    type: &quot;object&quot;,<br>    properties: {<br>      variables: {<br>        type: &quot;array&quot;,<br>        items: { key: &quot;string&quot;, value: &quot;string&quot; }<br>      },<br>      configs: {<br>        type: &quot;array&quot;,<br>        items: { name: &quot;string&quot;, value: [&quot;number&quot;, &quot;boolean&quot;] }<br>      }<br>    }<br>  }<br>}</pre><ul><li><strong>reset_theme</strong> — clears custom styling and restores defaults</li></ul><pre>{<br>  type: &quot;function&quot;,<br>  function: {<br>    name: &quot;reset_theme&quot;,<br>    description: &quot;Reset the current Gantt theme by clearing all custom CSS variables and configs (back to defaults).&quot;,<br>    parameters: {<br>      type: &quot;object&quot;,<br>      properties: {},<br>      required: [],<br>    },<br>  },<br>},</pre><p>These schemas are defined in the <a href="https://github.com/DHTMLX/gantt-theme-builder-ai-demo/blob/main/backend/schemaList.js">schemaList.js</a> file included with the demo project.</p><h4>Building Context for the AI Model</h4><p>Before calling the LLM, the backend constructs a system prompt that provides the necessary context while enforcing boundaries. This prompt includes:</p><ul><li>A description of all supported CSS variables</li><li>A complete list of available <strong>gantt.config</strong> options</li><li>The current theme and configuration state</li><li>Explicit rules governing how changes may be applied</li></ul><p>This context is generated by the <strong>generateSystemPrompt</strong> function and assembled into the final request by <a href="https://github.com/DHTMLX/gantt-theme-builder-ai-demo/blob/4a5fd4f77a5320f2071c9f5605f60829cc10db03/backend/server.js#L86">talkToLLM</a>.</p><pre>```<br>const messages = [<br>  { role: &quot;system&quot;, content: generateSystemPrompt(theme, configs) },<br>  { role: &quot;user&quot;, content: request },<br>];<br><br>const res = await openai.chat.completions.create({<br>  model: &quot;gpt-5-nano&quot;,<br>  messages,<br>  tools: schemaList,<br>});<br>```</pre><h4>Processing Structured AI Responses</h4><p>The AI model may return:</p><ul><li>A regular assistant message with no actionable output</li><li>A <strong>tool call</strong> containing structured instructions for modifying the Gantt chart</li></ul><pre>{<br>  &quot;tool&quot;: &quot;set_theme&quot;,<br>  &quot;variables&quot;: [<br>    { &quot;key&quot;: &quot;--dhx-gantt-task-background&quot;, &quot;value&quot;: &quot;#dce8ff&quot; },<br>    { &quot;key&quot;: &quot;--dhx-gantt-grid-background&quot;, &quot;value&quot;: &quot;#f9fbff&quot; }<br>  ],<br>  &quot;configs&quot;: [<br>    { &quot;name&quot;: &quot;row_height&quot;, &quot;value&quot;: 42 }<br>  ]<br>}</pre><p>When a tool call is present, the backend serializes the instructions and emits them to the frontend via a <strong>tool_call</strong> WebSocket event.</p><h3>Executing AI Instructions in the Browser</h3><p>At this stage, all logic runs entirely on the client side.</p><p>The frontend listens for incoming <strong>tool_call</strong> events and extracts the command payload:</p><pre>```<br>socket.on(&quot;tool_call&quot;, (callJSON) =&gt; {<br>  const { cmd, params } = JSON.parse(callJSON);<br>  runCommand(cmd, params);<br>});<br>```</pre><p>The extracted instructions are then executed using the <a href="https://github.com/DHTMLX/gantt-theme-builder-ai-demo/blob/4a5fd4f77a5320f2071c9f5605f60829cc10db03/frontend/src/command-runner.js"><strong>runCommand</strong></a> function:</p><pre>export default function (gantt, { onThemeSet, onConfigSet }, defaultConfigValues = {}) {<br>  // handlers for commands defined in /backend/schemaList.js<br>  return function runCommand(cmd, args) {<br>    switch (cmd) {<br>      case &quot;reset_theme&quot;:<br>        const themeStyleId = &quot;dynamic-theme&quot;;<br>        const styleEl = document.getElementById(themeStyleId);<br>        if (styleEl) {<br>          styleEl.remove();<br>        }<br><br>        if (onThemeSet) {<br>          onThemeSet([], cmd);<br>        }<br>        if (onConfigSet) {<br>          onConfigSet([], cmd);<br>          if (defaultConfigValues.length) {<br>            defaultConfigValues.forEach((config) =&gt; {<br>              gantt.config[config.name] = config.value;<br>            });<br>          }<br><br>          gantt.render();<br>        }<br><br>        break;<br>// ... other commands<br><br>      default:<br>        console.warn(&quot;Unknown cmd:&quot;, cmd, args);<br>    }<br>  };<br>}</pre><p>This unified command runner ensures that both AI-generated and manually edited updates follow the same execution path.</p><p>As a result, the Gantt chart immediately reflects the requested visual or configuration changes, providing instant feedback to the user.</p><p>For full implementation details and source code, refer to the <a href="https://github.com/DHTMLX/gantt-theme-builder-ai-demo">official GitHub repository</a>.</p><h3>Extending the Demo Toward Production Use</h3><p>The AI Gantt Theme Builder is intentionally minimal, serving as a reference architecture rather than a finished product. If you plan to build on this approach, several enhancements are worth considering:</p><ul><li><strong>Validation</strong><br> Introduce strict schema and whitelist checks for CSS variables and configuration parameters, including range and type validation.</li><li><strong>Ambiguity Handling</strong><br> Add confirmation steps for vague or potentially conflicting user requests to prevent unintended changes.</li><li><strong>Conversation Memory</strong><br> Persist conversation history to give the AI more contextual awareness across multiple interactions.</li><li><strong>Advanced Features</strong><br> Preview modes, collaborative editing, exportable theme packages, and richer chat interfaces (voice input, file uploads) can significantly improve usability.</li></ul><p>Together, these improvements can turn the demo into a robust environment for AI-assisted configuration and visualization of complex Gantt interfaces.</p><h3>Final Thoughts</h3><p>In this tutorial, you’ve learned how to build an AI-assisted Gantt Theme Builder that interprets natural-language prompts and applies visual updates to DHTMLX-based Gantt charts.</p><p>This approach can be extended with validation, smarter handling of ambiguous requests, and additional AI-driven features as your project requirements evolve. The underlying pattern — natural language → structured JSON → UI update — is versatile and can be applied to other scenarios, such as switching between calendars, toggling auto-scheduling modes, and applying predefined configuration presets.</p><h3>A message from our Founder</h3><p><strong>Hey, </strong><a href="https://linkedin.com/in/sunilsandhu"><strong>Sunil</strong></a><strong> here.</strong> I wanted to take a moment to thank you for reading until the end and for being a part of this community.</p><p>Did you know that our team run these publications as a volunteer effort to over 3.5m monthly readers? <strong>We don’t receive any funding, we do this to support the community. ❤️</strong></p><p>If you want to show some love, please take a moment to <strong>follow me on </strong><a href="https://linkedin.com/in/sunilsandhu"><strong>LinkedIn</strong></a><strong>, </strong><a href="https://tiktok.com/@messyfounder"><strong>TikTok</strong></a>, <a href="https://instagram.com/sunilsandhu"><strong>Instagram</strong></a>. You can also subscribe to our <a href="https://newsletter.plainenglish.io/"><strong>weekly newsletter</strong></a>.</p><p>And before you go, don’t forget to <strong>clap</strong> and <strong>follow</strong> the writer️!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8fa713672627" width="1" height="1" alt=""><hr><p><a href="https://ai.plainenglish.io/how-to-add-ai-driven-styling-to-dhtmlx-gantt-8fa713672627">How to Add AI-Driven Styling to DHTMLX Gantt</a> was originally published in <a href="https://ai.plainenglish.io">Artificial Intelligence in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[JavaScript News Digest: Latest Innovations, Updates, and Learning Resources]]></title>
            <link>https://itnext.io/javascript-news-digest-latest-innovations-updates-and-learning-resources-6d1e0cfef705?source=rss-e9c0a531d5e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/6d1e0cfef705</guid>
            <category><![CDATA[tips]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[news]]></category>
            <dc:creator><![CDATA[JavaScript UI Libraries — DHTMLX]]></dc:creator>
            <pubDate>Wed, 03 Dec 2025 12:26:52 GMT</pubDate>
            <atom:updated>2025-12-03T19:08:17.274Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*HRp4T-ZKedt8idwTI0Cikg.png" /></figure><p>Greetings, developers and JavaScript enthusiasts! We are back to continue sharing the latest achievements of the DHTMLX team and beyond. At DHTMLX, the past month was marked by the release of DHTMLX Diagram 6.1 and three new demo projects. This digest also covers notable industry updates such as Angular 21, Google’s new Antigravity development platform, and React File Manager from SVAR. Plus, we’ve gathered useful resources to deepen your knowledge in various aspects of modern web development.</p><h3>New Releases and Updates</h3><h4>Introducing DHTMLX Diagram 6.1</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*ZZtwLhNffZbYYuuFHzPUEA.png" /></figure><p>The strong data visualization capabilities of our <a href="https://dhtmlx.com/docs/products/dhtmlxDiagram/">JavaScript diagramming component</a> have proven to be valuable in many industries, including project management. For instance, the planning stage of many projects includes visualizations of team structures (org chart), project life cycle (flowchart), and decision-making logic (decision tree).</p><p>We are excited to present the DHTMLX Diagram 6.1 release, which includes a new mode for planning and organizing project delivery using PERT charts. The mode offers a built-in arrangement algorithm and predefined UI elements that streamline the visualization of project workflows with PERT charts. But the most peculiar thing about this novelty is the possibility to synchronize PERT charts with Gantt charts built with DHTMLX for a more complete project presentation.</p><p>Another part of this release is dedicated to new configuration options for keyboard hotkeys and shape manipulations in the DHTMLX Diagram editor. On top of that, the release is also notable for numerous live samples illustrating the practical value of the novelties introduced in v6.1. For complete information about this update, check out the <a href="https://dhtmlx.com/blog/introducing-dhtmlx-diagram-6-1/">release article</a>.</p><h4>Discovering New Possibilities for DHTMLX Gantt and Diagram with AI</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*L78BWguZlQDapnHoaqWotQ.png" /></figure><p>Once you start experimenting with AI enhancements for web development, it becomes enjoyable to see how seemingly tedious tasks become a no-brainer with the help of LLMs. The DHTMLX team is happy to present <a href="https://dhtmlx.com/blog/meet-new-demos-showcasing-ai-enhances-dhtmlx-gantt-diagram/">three new demos</a> that provide intelligent AI assistants to our JavaScript Gantt and Diagram components. The <a href="https://dhtmlx.com/docs/demo/ai-gantt-maker/">AI Gantt Maker</a> and <a href="https://dhtmlx.com/docs/demo/ai-gantt-theme-builder/">AI Gantt Theme Builder</a> enhance the capabilities of our flagship product in creating project structures and styling Gantt charts. The <a href="https://dhtmlx.com/docs/demo/ai-org-chart-builder/">AI Org Chart Builder</a> greatly simplifies visualizing company hierarchies of any complexity. Each demo follows a similar interaction model based on transforming natural language commands (prompts) into LLM-based instructions applied to the UI in real time.</p><h4>SVAR React File Manager Now Available</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*1U4Usgky-_J502khXqZFjA.png" /></figure><p>SVAR UI released a <a href="https://svar.dev/blog/react-filemanager-released/">new React File Manager</a>, an open-source file explorer component with a familiar desktop-like interface. It supports all essential file operations, multiple view modes, file preview sidebar, intuitive navigation, and search capabilities.</p><p>Built with React 19 and full TypeScript support, SVAR File Manager helps you create a clean UI for navigating and managing file systems on the web. Available <a href="https://github.com/svar-widgets/react-filemanager">on GitHub</a> under the MIT license.</p><h4>What’s New in Angular 21</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*CUbiPRnjwuHpaMflKMgHjw.png" /></figure><p>We are not the only ones among software vendors taking practical steps towards smooth adaptation to a more AI-affected web development environment. The Angular team has recently presented the 21st iteration of its framework, which now has a stable MCP server, ensuring AI agents have all the context they need for Angular development. Apart from that, the update includes new experimental Signal Forms that provide a reactive approach to form state management with strong type safety. Improved zoneless change detection contributes to better performance and developer experience. Also, Angular developers now have access to a library of headless UI patterns (Angular Aria) and Vitest as a default test runner. More details about Angular 21 are provided in the <a href="https://blog.angular.dev/announcing-angular-v21-57946c34f14b">release article</a>.</p><h4>Meet Antigravity: New Agentic Development Platform from Google</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*8NFXvi1M6Ep_08IAQv5hvQ.png" /></figure><p>Google has recently announced the release of its latest solution, <a href="https://antigravity.google/blog/introducing-google-antigravity">Antigravity</a>, an innovative, agent-first IDE designed to expand the horizons of software development with AI technologies. It provides developers with two modes for working with code: the Editor view and the Manager surface.</p><p>If the first one is a familiar AI-enhanced code editor, the second one is a real difference-maker that helps Antigravity to stand out. The Manager surface represents a new interface for managing multiple agents that work asynchronously on different tasks across the IDE, terminal, and browser. Agents generate tangible deliverables called Artifacts (screenshots, walkthroughs, browser recordings, etc.) that make it much easier for developers to verify the results of their work. Antigravity also allows agents to accumulate knowledge from previous tasks. The platform has received mixed reviews following reports of security concerns, yet it is too early to make definitive judgments about Antigravity’s future.</p><h3>Useful Tips and Articles</h3><h4>Overview of Top Scheduler Components for React</h4><p>When requested to implement complex functionality such as a scheduling calendar, many dev teams reasonably prefer using specialized UI components to speed up the development process. But choosing the right tool for this purpose is a non-trivial task that requires a serious and thorough approach. If that is your case, this <a href="https://dhtmlx.com/blog/best-react-scheduler-components-dhtmlx-bryntum-syncfusion-daypilot-fullcalendar/">blog post</a> offers an overview of popular React Schedulers from DHTMLX, Bryntum, Syncfusion, DayPilot, and FullCalendar. Here, you will learn about the distinguishing features of these components and their licensing/pricing policies, which should help make the right choice.</p><h4>What Does It Take to Write Effective agents.md Files for GitHub Copilot</h4><p>Since recently, developers have been given the opportunity to add several custom agents in agents.md files of GitHub Copilot. In other words, it is possible to create a team of helpers for different tasks, from technical writing to QA and security analysis. Sounds like a great productivity booster, but in practice, many agent files do not work as desired because of their vagueness. This <a href="https://github.blog/ai-and-ml/github-copilot/how-to-write-a-great-agents-md-lessons-from-over-2500-repositories/">post</a> explains how to properly create custom agents based on the analysis of over 2,500 agents.md files across public repos.</p><h4>Quick Overview of 18 Useful TypeScript Utility Types</h4><p>When it comes to transforming and manipulating data types in TypeScript, you will certainly need to use utility types. These predefined constructs allow creating new types by altering existing ones, thereby enhancing code readability and maintainability. This <a href="https://www.youtube.com/watch?v=BhNSauna0eo">video</a> highlights 18 popular utility types and their use case scenarios.</p><h4>Six Nontrivial Tricks for Working with Chrome’s DevTools</h4><p>If you think that the Chrome Developer Tools (aka DevTools) have nothing left to surprise you, think again. Most of the online tutorials on DevTools focus on popular features such as Elements, Console Logs, Performance panel, etc., but there is much more under the hood. DevTools also include many lesser-known, but still impactful capabilities, such as monitoring any function, watching DOM elements for changes, recording and replaying user actions, etc. If you are interested in expanding your knowledge about such DevTools features, check out the material (<a href="https://www.readwriterachel.com/things-i-learned/2025/11/09/devtools-1.html">part 1</a> and <a href="https://www.readwriterachel.com/things-i-learned/2025/11/17/devtools-2.html">part 2</a>) from Rachel Kaufman.</p><h4>Comparative Table of Over 100 JavaScript Engines</h4><p>An average web developer can name 3 or 4 popular JS engines. This list will likely include V8, SpiderMonkey, JavaScriptCore, and Chakra. These engines are widely known and used in major browsers and runtimes today. Those who want to delve into the subject and get acquainted with many other JS engines, have a good chance to do that thanks to the <a href="https://zoo.js.org/">JavaScript Engines Zoo</a> project. It provides a large comparative table of known JavaScript engines.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6d1e0cfef705" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/javascript-news-digest-latest-innovations-updates-and-learning-resources-6d1e0cfef705">JavaScript News Digest: Latest Innovations, Updates, and Learning Resources</a> was originally published in <a href="https://itnext.io">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>