Home / Articles

Use DHTMLX React Gantt with Remix for project planning apps

DHTMLX React Gantt inside Remix routes: stable config, data.save callbacks and optional imperative API access for project planning, with no SSR workarounds.

Publisher: REACTAPP.TOP5 min read1037 words

This article is published in English.

Sources: Using DHTMLX React Gantt and Remix for Project Planning Apps

Project-planning UIs in React usually fight two pressures at once: dense interactive scheduling on the client, and data that should arrive without stalling the chart. As task graphs grow and workflows branch, keeping a Gantt responsive depends as much on how routes load data as on the chart library itself.

Remix sits on the server-render side of that trade-off. Paired with DHTMLX React Gantt, it gives planning screens a path to load work efficiently while still leaving the timeline free to pan, edit, and link tasks. The public starter on GitHub under DHTMLX/react-gantt-remix-starter is a concrete walkthrough of those integration seams: https://github.com/DHTMLX/react-gantt-remix-starter

What Remix Adds to a React Gantt Application

Earlier write-ups already pair heavy scheduling widgets with Next.js SSR. Remix attacks the same integration with a different bias: it leans on familiar web APIs and React Router instead of inventing a parallel data-fetching model.

That choice makes routing and data movement easier to read in the source tree. For Gantt charts, schedulers, and other data-heavy dashboards, interactions often need a clear line back to server operations, so an explicit route-and-loader shape helps more than opaque framework magic.

The starter focuses on how rendering flows between Remix and React Gantt. It covers:

  • loading data from the route
  • keeping Gantt configuration referentially stable
  • pushing UI edits back out of the chart
  • reaching the Gantt API when props are not enough

Loaders, actions, and persistence are deliberately out of scope. Those pieces change with each product architecture. The demo’s job is to show that DHTMLX React Gantt drops into Remix’s render model without SSR-only wrappers or special casing.

From version 9.0.12 onward, DHTMLX React Gantt works with SSR frameworks such as Next.js and Remix without extra client-only shells or one-off configuration.

Integration Details: Connecting Remix and DHTMLX React Gantt

The practical wiring points below are what make Remix plus React Gantt behave in a real route.

Rendering Gantt Inside a Remix Route

The chart lives in its own GanttChart component. From Remix you import that component into an ordinary route and render it like any other React tree—no special route API is required.

import GanttChart from '~/components/Gantt/Gantt';
import type { Route } from './+types/home';
import { tasks, links } from '~/data/demoData';

export default function Home() {
  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <GanttChart tasks={tasks} links={links} />
    </div>
  );
}

The route owns page layout and data flow. React Gantt owns drawing the timeline and handling edits. Tasks and links arrive as props; mutations leave through the save callback.

The sample uses static demo data so the shape stays obvious. The same prop path works when Remix loaders supply server-fetched tasks and links.

One easy miss: the Gantt host needs an explicit size. Without a set height (for example width: '100vw', height: '100vh'), the component can mount while the chart remains blank.

Keeping the Gantt Configuration Stable

GanttConfig drives scales, dates, grid width, and related UI knobs.

Plain objects allocated during render get a new reference every pass. Feeding a fresh config object into Gantt can force a full reinitialization. The demo freezes the config with useMemo:

const config: GanttConfig = useMemo(
  () => ({
    grid_width: 500,
    scale_height: 90,
    scales: [
      { unit: 'year', step: 1, date: '%Y' },
      { unit: 'month', step: 1, date: '%M' },
      { unit: 'day', step: 1, date: '%d %M' },
    ],
  }),
  []
);

The memoized object is created once and reused for the component’s life, so incidental parent renders do not rebuild the chart.

Processing User Changes with data.save

A Gantt is an editor, not only a picture. Users add tasks, shift dates, rewire dependencies, and delete rows.

DHTMLX React Gantt surfaces those edits through data.save:

data={{
  save: (entity: string, action: string, data: Task | Link, id: string | number) => {
    console.log(`${entity} - ${action} - ${id}`, data);
  },
}}

In the starter, the callback only logs. In production, this is where Remix actions, HTTP calls, or local state updates would persist the change. The callback is the bridge from chart gestures into your data layer; the exact destination depends on the app’s architecture.

Accessing the Gantt API When Needed

Some flows need more than declarative props: scrolling the timeline in code, reading a task payload, or attaching custom behavior.

The demo holds the Gantt instance with useRef:

const ganttRef = useRef<ReactGanttRef>(null);

const gantt = ganttRef.current?.instance;
if (!gantt) return;

// here you can call any Gantt API method
console.log('All tasks:', gantt.getTaskByTime());
gantt.showDate(new Date());

That ref reaches the underlying API without forcing React re-renders. Simple charts may never need it. As product rules grow, imperative access keeps custom interactions practical.

With those seams clear, the starter repository and the Remix integration guide at https://docs.dhtmlx.com/gantt/integrations/react/remix/ are easier to follow end to end.

Wrapping Up

The Remix starter is not a full product architecture. It shows how render flow, data hand-off, and Gantt interactions can sit inside a Remix project next to a rich scheduling widget.

From there you can add server persistence, introduce a store, or extend the chart with custom logic. Together with the docs guide, the demo is a workable base for SSR-friendly project-planning UIs.