React Gantt ist verfügbar unter Commercial, Enterprise und Ultimate licenses. Für Benutzer der Individual- oder GPL-Editionen von Gantt beachten Sie bitte die Anleitung How to Start für React.
DHTMLX Gantt ist eine reine JavaScript-Komponente, die mit jeder Browserumgebung kompatibel ist. Die Commercial- und höherwertigen Editionen beinhalten eine React Gantt-Komponente, die DHTMLX Gantt umschließt und so eine einfache native Integration in React-Anwendungen ermöglicht.
Dieser Wrapper erlaubt es, ein voll funktionsfähiges Gantt-Diagramm mit dem bekannten Props- und State-Modell von React zu erstellen. Intern verwaltet er eine Standard-Instanz von DHTMLX Gantt und wandelt React-Props (wie tasks und config) in die entsprechenden Gantt-Initialisierungen und Datenstrukturen um.
Hauptfunktionen
Für Einsteiger in DHTMLX Gantt bietet die DHTMLX Gantt Dokumentation einen Überblick über Funktionen wie Arbeitszeitberechnung, Auto Scheduling, Ressourcenmanagement und mehr.
Installation der Testversion der React Gantt-Komponente
Um die Testversion der React Gantt-Komponente auszuprobieren, laden Sie das DHTMLX Gantt-Testpaket von hier herunter und folgen Sie den Anweisungen in der README-Datei. Das Paket enthält auch React Gantt-Beispiele. Beachten Sie, dass die Testversion nur 30 Tage gültig ist.
Installation der PRO-Version der React Gantt-Komponente
Der Zugang zum privaten DHTMLX npm ist über den Client's Area verfügbar, wo Sie Ihren npm-Login und Ihr Passwort generieren können. Dort finden Sie eine ausführliche Installationsanleitung. Beachten Sie, dass der Zugriff auf das private npm eine aktive proprietäre Gantt-Lizenz erfordert.
v18.0.0
oder höherNachfolgend ein einfaches Beispiel, das zeigt, wie das Gantt-Diagramm importiert und gerendert wird:
import { useState } from 'react';
import ReactGantt from '@dhx/react-gantt';
import '@dhx/react-gantt/dist/react-gantt.css';
import { demoData } from './DemoData'
export default function BasicGantt() {
const [theme, setTheme] = useState("terrace");
const [tasks, setTasks] = useState(demoData.tasks);
const [links, setLinks] = useState(demoData.links);
return (
<div style={ { height: '500px' } }>
<ReactGantt
tasks={tasks}
links={links}
theme={theme}
/>
</div>
);
}
Das demoData-Objekt folgt diesem Format:
const demoData = {
tasks: [
{ id: 1, text: "Product Launch", type: "project", open: true, parent: 0},
{ id: 2, text: "Planning Phase", type: "project", open: true, parent: 1},
{ id: 3, text: "Requirement Gathering", type: "task", progress: 0.2,
start_date: "2025-06-01", duration: 3, parent: 2},
{ id: 4, text: "Technical Feasibility", type: "task", progress: 0.4,
start_date: "2025-06-04", duration: 2, parent: 2},
{ id: 5, text: "Implementation Phase", type: "project", progress: 0.1,
open: true, start_date: "2025-06-08", duration: 10, parent: 1},
{ id: 6, text: "Prototype Development", type: "task", progress: 0.0,
start_date: "2025-06-08", duration: 4, parent: 5},
{ id: 7, text: "Feature Testing", type: "task", progress: 0.0,
start_date: "2025-06-12", duration: 4, parent: 5},
{ id: 8, text: "Go-Live Milestone", type: "milestone", progress: 0,
start_date: "2025-06-18", duration: 0, parent: 1}
],
links: [
{ id: 1, source: 3, target: 4, type: "0" },
{ id: 2, source: 4, target: 5, type: "0" },
{ id: 3, source: 6, target: 7, type: "0" },
{ id: 4, source: 7, target: 8, type: "0" }
]
};
export demoData;
Der ReactGantt-Wrapper unterstützt flexible Optionen zum Laden und Speichern von Daten. Es gibt zwei Hauptmethoden, um Änderungen an Ihren Gantt-Daten zu verwalten:
Beide Methoden funktionieren gut, aber es ist ratsam, sich für eine zu entscheiden und dabei zu bleiben, um unerwartete Probleme zu vermeiden.
Bei diesem Ansatz liest ReactGantt alle Aufgaben- und Linkdaten aus Ihrem React-State. Wenn Benutzer Aufgaben oder Links im Gantt bearbeiten (z. B. Hinzufügen oder Löschen einer Aufgabe), wird ein Callback ausgelöst. In diesem Callback aktualisieren Sie Ihren React-State mit den Änderungen. Sobald sich der State ändert, rendert React ReactGantt neu, das dann die neuesten Daten aus dem State lädt.
function MyGanttApp() {
const [tasks, setTasks] = useState<Task[]>(initialTasks);
const [links, setLinks] = useState<Link[]>(initialLinks);
const data = {
save: (entity: string, action: string, raw: any, id: string | number) => {
if (entity === 'task') {
if (action === 'create') {
setTasks((prev) => [...prev, item]);
} ...
}
...
}
};
return (
<ReactGantt
tasks={tasks}
links={links}
data={data}
// ...other props
/>
);
}
In diesem Beispiel ruft ReactGantt den save-Callback auf, wenn eine Aufgabe erstellt wird, und der React-State wird entsprechend aktualisiert. Die Änderung des States führt dazu, dass ReactGantt die Gantt-Daten neu initialisiert.
Dieses Muster hält den React-State als einzige Quelle der Wahrheit für UI- und Server-Updates und passt sich natürlich der Logik von React oder Redux an.
Beachten Sie, dass dies zu häufigeren Parsings oder Neurenderings des Gantt führen kann.
Hier finden Änderungen direkt in der Gantt-Instanz statt, ohne dass sie zwingend mit dem React-State synchronisiert werden müssen. Sie können Aufgaben und Links initial laden (über Props oder den eingebauten Data Processor von Gantt), aber nach dem Start verwaltet Gantt die Daten intern. Wenn Sie einen Update-Callback einrichten oder den integrierten Transport verwenden, sendet Gantt Änderungen an einen Server oder eine benutzerdefinierte Funktion, aktualisiert oder setzt den React-State jedoch nicht automatisch nach Änderungen zurück.
<ReactGantt
data={ {
load: "/api/data", // gantt lädt die initialen tasks/links von hier
save: "/api/data" // gantt sendet Updates hierhin zurück
} }
/>
In diesem Setup übernimmt Gantt das Laden und Speichern der Daten eigenständig, wobei die lokale Gantt-Instanz als Hauptdatenquelle dient.
Dies reduziert den Overhead durch ständige React-State-Updates während Gantt-Änderungen und vereinfacht Massenoperationen wie Auto Scheduling, ohne wiederholte Neurenderings.
Der Nachteil ist der Verlust der direkten Synchronisation zwischen Gantt-Daten und React-State. Wenn Sie tasks/links dennoch im React-State halten, achten Sie darauf, die internen Daten von Gantt nicht versehentlich zu überschreiben.
Wenn die Daten im Code verfügbar sind, können sie Gantt über State-Variablen und die entsprechenden Props übergeben werden:
export default function GanttTemplatesDemo() {
const [tasks, setTasks] = useState(projectData.tasks);
const [links, setLinks] = useState(projectData.links);
const [resources, setResources] = useState(projectData.resources);
const [resourceAssignments, setResourceAssignments] =
useState(projectData.resourceAssignments);
return (
<div style={ {height: '100vh'} }>
<ReactGantt
tasks={tasks}
links={links}
resources={resources}
resourceAssignments={resourceAssignments}
/>
</div>
);
};
Sie können eine URL angeben, von der Gantt Daten lädt, sowie eine weitere URL, an die Updates gesendet werden:
import React from 'react';
import ReactGantt from "@dhx/react-gantt";
import "@dhx/react-gantt/dist/react-gantt.css";
export default function BasicInitDemo() {
const props = {
data: {
load: "/api/data",
save: "/api/data"
}
}
return (
<ReactGantt ...{props} />
);
}
Intern wird die load-URL an die Methode load übergeben. Die Endpoint-Antwort sollte wie im Artikel Datenladen beschrieben formatiert sein.
Die save-URL erhält Updates im in diesem Artikel beschriebenen Format.
Alternativ können Sie eine Funktion als save-Eigenschaft des data-Objekts bereitstellen. Diese Funktion wird immer dann aufgerufen, wenn sich Gantt-Daten ändern, und dient als Routing-Handler für den internen DataProcessor:
import React from 'react';
import ReactGantt from "@dhx/react-gantt";
import "@dhx/react-gantt/dist/react-gantt.css";
export default function BasicInitDemo() {
const props = {
data: {
load: "/api/data",
save: (entity, action, data, id) => {
console.log(`${entity} - ${action} - ${id}`, data);
}
}
};
return (
<ReactGantt ...{props} />
);
}
In früheren Modi löste React Gantt den Callback für jede geänderte Entität einzeln aus. Dies entspricht dem Standardverhalten der zugrunde liegenden Gantt-Bibliothek. Dies kann jedoch zu Performance-Einbußen in React führen, insbesondere bei Massenoperationen wie Auto Scheduling, bei denen Dutzende oder sogar Hunderte von Aufgaben gleichzeitig aktualisiert werden. Das Verarbeiten von State-Updates für jede Änderung einzeln ist in solchen Fällen nicht effizient.
Um dies zu verbessern, bietet React Gantt einen speziellen data.batchSave-Handler für Massenupdates.
Dieser Handler wird einmalig mit allen vorgenommenen Änderungen der Gantt-Instanz aufgerufen:
const [tasks, setTasks] = useState(data.tasks);
const [links, setLinks] = useState(data.links);
return <ReactGantt
ref={ganttRef}
tasks={tasks}
links={links}
data={ {
batchSave: (updates) => {
if (updates.task) {
setTasks(tasks => updateTasks(tasks, updates.task));
}
if (updates.link) {
setLinks(links => updateLinks(links, updates.link));
}
}
} }
/>
Das updates
-Objekt, das an den batchSave-Callback übergeben wird, sieht folgendermaßen aus:
{
tasks: DataCallbackChange<Task>[],
links: DataCallbackChange<Link>[],
resources: DataCallbackChange<Resource>[],
resourceAssignments: DataCallbackChange<ResourceAssignment>[],
}
interface DataCallbackChange<T> {
entity: string;
action: string;
data: T;
id: number | string;
}
Der React-Wrapper akzeptiert eine config
-Prop (die auf gantt.config abbildet) und eine templates
-Prop (die auf gantt.templates abbildet).
<ReactGantt
tasks={tasks}
links={links}
config= { {
scales: [
{ unit: "year", step: 1, format: "%Y" },
{ unit: "month", step: 1, format: "%F, %Y" },
{ unit: "day", step: 1, format: "%d %M" },
],
columns: [
{ name: "text", tree: true, width: "*", resize: true },
{ name: "start_date", align: "center", resize: true },
{ name: "duration", align: "center", resize: true },
{
name: "custom",
align: "center",
template: (task) => <AlertButton task={task} onClick={handleButtonClick} />,
resize: true,
},
{ name: "add", width: 44 },
],
} }
templates= { {
task_text: (start, end, task) => `#${task.id}: ${task.text}`,
task_class: (start, end, task) => {
return task.priority === 'high' ? 'highlight-task' : '';
},
} }
/>
Wenn Templates in Props definiert werden, können React-Elemente von den Template-Funktionen zurückgegeben werden:
function PriorityBadge({ priority }) {
return <span style={ { color: 'red' } }>{priority}</span>;
}
<ReactGantt
templates={ {
task_text: (start, end, task) => {
return <PriorityBadge priority={task.priority} />;
}
} }
/>
Intern arbeitet DHTMLX Gantt mit dem DOM auf eine Weise, die React nicht direkt verwendet. Wenn React-Komponenten von Templates zurückgegeben werden, werden sie mithilfe von Portalen in das HTML von Gantt eingebettet. Beachten Sie, dass das Rendern komplexer React-Komponenten bei großen Datenmengen die Performance beeinflussen kann.
Templates können zur Anpassung vieler Bereiche verwendet werden:
Die vollständige Liste der von React Gantt unterstützten Props finden Sie unter: Verwendung von DHTMLX Gantt Properties in ReactGantt
Gantt enthält mehrere eingebaute Themes, die über die theme-Prop gesetzt und dynamisch gewechselt werden können:
import { useEffect, useRef } from 'react';
import ReactGantt from "@dhx/react-gantt";
import "@dhx/react-gantt/dist/react-gantt.css";
export default function BasicInitDemo() {
const [theme, setTheme] = useState("terrace");
const tasks = [...];
const links = [...];
const switchTheme = () => {
setTheme((prevTheme) => (prevTheme === "terrace" ? "dark" : "terrace"));
};
return (
<div style={ {height: '600px'} }>
<div>
<button onClick={switchTheme}>Switch Theme</button>
</div>
<ReactGantt
tasks={tasks}
links={links}
theme={theme} />
</div>
);
};
Ausführliche Beschreibungen der verfügbaren Themes finden Sie in diesem Artikel.
Themes können zusätzlich durch eigene Styles oder das Überschreiben von CSS-Variablen angepasst werden:
:root {
--dhx-gantt-task-background: #d96c49;
--dhx-gantt-task-color: #fff;
--dhx-gantt-task-border-radius: 8px;
}
Weitere Konfigurationsmöglichkeiten finden Sie im Leitfaden Skins-Anpassung.
DHTMLX Gantt enthält einen eingebauten, konfigurierbaren Task-Editor namens Lightbox.
Falls gewünscht, kann dieser durch ein React-Modal oder eine andere Komponente auf folgende Weisen ersetzt werden:
customLightbox
PropSie können eine Komponente über die customLightbox-Prop übergeben:
import React, { useState } from 'react';
export interface CustomLightboxProps {
data: any;
onSave: (task: any) => void;
onCancel: () => void;
onDelete: () => void;
}
const CustomLightbox: React.FC<CustomLightboxProps> = ({
data,
onSave,
onCancel,
onDelete
}) => {
const [description, setDescription] = useState<string>(data.text || '');
const handleSaveClick = () => {
onSave({ ...data, text: description });
};
const modalStyles = {
...
};
return (
<div>
<div style={modalStyles.overlay} onClick={onCancel} />
<div style={modalStyles.content}>
<h3>Edit Task</h3>
<div>
<label>Description:</label>
<input
type="text"
value={description}
onChange={(e) => setDescription(e.target.value)}
style={ { width: '100%', padding: '8px', marginTop: '10px' } }
/>
</div>
<div style={modalStyles.buttonGroup}>
<button onClick={handleSaveClick}>Save</button>
<button onClick={onCancel}>Cancel</button>
<button onClick={onDelete}>Delete</button>
</div>
</div>
</div>
);
};
export default CustomLightbox;
Sie können diese Komponente dann wie folgt verwenden:
import { useEffect, useRef } from 'react';
import ReactGantt from "@dhx/react-gantt";
import "@dhx/react-gantt/dist/react-gantt.css";
import CustomLightbox from "./EditorModal";
export default function BasicInitDemo() {
const ganttRef = useRef(null);
const tasks = [...];
const links = [...];
useEffect(() => {
//const gantt = ganttRef.current?.instance;
}, []);
return (
<ReactGantt
ref={ganttRef}
tasks={tasks}
links={links}
customLightbox={<CustomLightbox />} />
);
}
Für fortgeschrittene Fälle können Sie das onBeforeLightbox Event abfangen (wird ausgelöst, wenn die Lightbox geöffnet wird) und das Standardverhalten überschreiben:
import { useEffect, useRef } from 'react';
import ReactGantt from "@dhx/react-gantt";
import "@dhx/react-gantt/dist/react-gantt.css";
import { useNavigate } from 'react-router-dom';
export default function BasicInitDemo() {
const ganttRef = useRef<any>(null);
const tasks = [...];
const links = [...];
const navigate = useNavigate();
const handleTaskEdit = (id: any) => {
const ganttInstance = ganttRef.current?.instance;
navigate(`/editor/${id}`, { state: { task: ganttInstance.getTask(id) } });
};
return (
<ReactGantt
ref={ganttRef}
tasks={tasks}
links={links}
onBeforeLightbox={handleTaskEdit} />
);
}
Weitere Informationen zum Überschreiben oder Erweitern der eingebauten Lightbox finden Sie unter Custom Lightbox.
Die Standard-Benutzeroberfläche enthält zwei Modaldialoge:
Beide können mit der modals
-Prop von ReactGantt angepasst werden:
<ReactGantt
...
modals={ {
onBeforeTaskDelete: ({
task,
callback,
ganttInstance,
}: {
task: Task;
callback: () => void;
ganttInstance: GanttStatic;
}) => void,
onBeforeLinkDelete: ({
link,
callback,
ganttInstance,
}: {
link: Link;
callback: () => void;
ganttInstance: GanttStatic;
}) => void,
} }
...
/>
Mit diesen Props können Sie eigene Modals anzeigen, wenn Gantt eine Bestätigung anfordert.
Das Aufrufen des bereitgestellten callback()
schließt das Löschen der Aufgabe oder des Links ab. Um abzubrechen, schließen Sie das Modal einfach, ohne den Callback aufzurufen.
Die label-Eigenschaft einer Grid-Spalte kann ein string
oder ein ReactElement
sein. So können Sie React-basierte Filter, Buttons oder andere UI-Elemente direkt in den Spaltenkopf einbetten:
const config: GanttConfig = {
columns: [
{ name: "text", label: "Name", tree: true, width: 180,
resize: true },
// React-Element direkt eingebettet
{ name: "start_date", label: <DateFilter />, width: 150,
align: "center", resize: true },
// Oder als Funktion, die ein React-Element zurückgibt:
{ name: "end_date", label: () => <DateFilter />, width: 150,
align: "center", resize: true },
...
],
row_height: 40,
grid_width: 550,
};
Wenn der Wrapper ein React-Element in einem Label oder einer anderen Template-Eigenschaft findet, rendert er dieses mithilfe eines React-Portals in die Header-Zelle des Grids.
Grid-Zellen werden von der template-Eigenschaft einer Spalte gesteuert. Diese Funktion erhält ein Task-Objekt und sollte entweder einen einfachen string
oder ein ReactElement
zurückgeben:
import { useRef } from 'react';
function AlertButton({ task, onClick }) {
return <button onClick={onClick}>{`Task ID: ${task.id}`}</button>;
}
export default function GanttWithGridCells({ handleButtonClick, ganttRef }) {
const config = {
columns: [
{ name: "text", tree: true, width: 180, resize: true },
{ name: "start_date", width: 150, align: "center", resize: true },
{ name: "duration", width: 80, align: "center", resize: true },
{
name: "custom",
align: "center",
label: <span>My Column</span>,
width: 140,
// Gibt ein React-Element zurück
template: (task) => (
<AlertButton
task={task}
onClick={() => {
handleButtonClick(task);
// Falls nötig, ein erneutes Rendern der Aufgabe auslösen
ganttRef.current?.instance.updateTask(task.id);
}}
/>
),
resize: true,
},
{ name: "add", width: 44 },
],
row_height: 40,
grid_width: 550,
};
return <ReactGantt ref={ganttRef} config={config} /* ...other props */ />;
}
Durch das Zurückgeben von React-Elementen aus Ihren Spaltentemplates können Sie vollständig interaktive Inhalte wie Buttons, Dropdowns oder Badges in jede Gantt-Grid-Zelle einbauen. Der Wrapper injiziert diese Elemente über Portale in die vom Gantt verwalteten DOM-Knoten.
DHTMLX Gantt unterstützt Inline-Bearbeitung für Rasterzellen. Innerhalb dieses React-Wrappers können benutzerdefinierte React-Editoren hinzugefügt werden, indem ein Editor-Objekt in der Spalten-Konfiguration definiert und ein Editor-Name über die inlineEditors
-Prop mit einer React-Komponente verknüpft wird. Das folgende Beispiel zeigt diese Einrichtung.
Definieren Sie eine React-basierte Inline-Editor-Komponente:
import React, {
useState,
forwardRef,
useImperativeHandle
} from 'react';
import { InlineEditorMethods, InlineEditorProps } from '@dhx/react-gantt';
const MyInlineEditor = forwardRef<InlineEditorMethods, InlineEditorProps>(
({ initialValue, task, save, cancel, ganttInstance }, ref) => {
const [value, setValue] = useState(initialValue || "");
useImperativeHandle(ref, (): InlineEditorMethods => ({
getValue: () => value,
setValue: (val: any) => setValue(val),
isValid: () => true,
focus: () => {
},
isChanged: (originalValue: any) => {
return originalValue !== value;
},
save: () => { }
}));
return (
<input
type="text"
value={value}
onChange={e => setValue(e.target.value)}
autoFocus
/>
);
}
);
export default MyInlineEditor;
Verwenden Sie den benutzerdefinierten Editor in Ihrer Gantt-Konfiguration:
import ReactGantt from "@dhx/react-gantt";
import MyInlineEditor from "./CustomInlineEditor";
function Demo() {
const config = {
columns: [
{ name: "text", tree: true, width: 180, resize: true },
{
name: "duration",
width: 80,
align: "center",
editor: { type: "customInputEditor", map_to: "text" }, resize: true
},
{ name: "start_date", width: 150 },
{ name: "add", width: 44 }
]
};
return (
<ReactGantt
config={config}
inlineEditors={ {
customInputEditor: MyInlineEditor } }
tasks={[/*...*/]}
links={[/*...*/]}
/>
);
}
Wenn ein Benutzer auf eine Spaltenzelle doppelklickt, ersetzt das Gantt-Diagramm diese durch Ihre Editor-Komponente. Der Wrapper ruft intern die von Ihnen bereitgestellten Methoden (getValue, setValue usw.) über useImperativeHandle(ref, ...)
auf und hält die Gantt-Instanz mit den Änderungen Ihrer Komponente synchron.
Der Wert von type
im Editor-Objekt muss dem Schlüssel in inlineEditors
entsprechen.
Die Eigenschaft map_to
gibt an, auf welche Eigenschaft des Task-Objekts der Editor zugreift und in diese schreibt. Weitere Informationen finden Sie im Artikel zur Inline-Bearbeitung.
Wenn Ihr Editor komplexere Operationen als das einfache Aktualisieren einer Task-Eigenschaft ausführt, implementieren Sie die notwendige Logik innerhalb der save-Funktion und setzen Sie die Option map_to
auf "auto". In diesem Modus aktualisiert das Gantt-Diagramm das Task-Objekt nicht automatisch, sondern ruft die save-Funktion auf, wenn Änderungen übernommen werden sollen. Der Editor erhält dann null
als initialValue
.
Hinweis: Nicht-React-Inline-Editoren können ebenfalls über die Eigenschaft editor_types innerhalb der config definiert werden.
Mit der filter
-Prop können Sie eine Funktion angeben, die steuert, welche Aufgaben sichtbar sind:
const [filter, setFilter] = useState<((task: Task) => boolean) | null>(null);
function showCompleted() {
setFilter(() => (task: Task) => task.progress === 1);
}
function resetFilter() {
setFilter(null);
}
return (
<ReactGantt
...
filter={filter}
...
/>
);
Um Ressourcen im Ressourcen-Panel zu filtern, verwenden Sie die Prop resourceFilter
:
function handleResourceSelectChange(resourceId: string | null) {
setSelectedResource(resourceId);
if (resourceId === null) {
setResourceFilter(null);
} else {
setResourceFilter(
() => (resource: ResourceItem) => String(resource.id) === String(resourceId)
);
}
}
return (
<ReactGantt
ref={ganttRef}
tasks={tasks}
links={links}
resources={resources}
resourceFilter={resourceFilter}
config={config}
templates={templates}
plugins={ { auto_scheduling: true } }
/>
);
Um Arbeitszeitberechnungen in ReactGantt zu ermöglichen, aktivieren Sie die Arbeitszeitfunktion in der Konfiguration:
const config: GanttConfig = {
...
work_time: true
};
Arbeitskalender können an ReactGantt über die Prop calendars
übergeben werden:
const calendars: Calendar[] = [
{
id: "global",
hours: ["8:00-12:00", "13:00-17:00"], // globale Arbeitszeiten an Wochentagen
days: {
weekdays: {
0: false, // 0 = Sonntag, 6 = Samstag
1: true,
2: true,
3: true,
4: true,
5: true,
6: false
},
dates: {
"2025-04-06": true, // Arbeitszeiten für ein bestimmtes Datum überschreiben
"2025-04-08": false
}
}
}
];
return (
<div style={ { height: '100%', display: 'flex', flexDirection: 'column' } }>
<ReactGantt
...
calendars={calendars}
...
/>
</div>
);
Um Arbeitszeiten im Gantt-Timeline hervorzuheben oder Arbeitszeitberechnungen durchzuführen, kann der bereitgestellte Hook useWorkTime
verwendet werden:
import ReactGantt, { useWorkTime, Calendar } from "@dhx/react-gantt";
export default function GanttTemplatesDemo() {
const ganttRef = useRef<ReactGanttRef>(null);
const { isWorkTime } = useWorkTime(ganttRef);
const templates: GanttTemplates = {
timeline_cell_class: (task: Task, date: Date) => {
return isWorkTime({ date, task }) ? "" : "weekend";
}
};
const calendars: Calendar[] = [
{
id: "global",
hours: ["8:00-12:00", "13:00-17:00"], // globale Arbeitszeiten an Wochentagen
days: {
weekdays: {
0: false, // 0 = Sonntag, 6 = Samstag
1: true,
2: true,
3: true,
4: true,
5: true,
6: false
},
dates: {
"2025-04-06": true, // Arbeitszeiten für ein bestimmtes Datum überschreiben
"2025-04-08": false
}
}
}
];
return (
<div style={ { height: '100%', display: 'flex', flexDirection: 'column' } }>
<ReactGantt
...
calendars={calendars}
templates={templates}
config={config}
ref={ganttRef}
/>
</div>
);
};
Alternativ kann das interne Gantt-Objekt für die direkte Nutzung von Arbeitszeit-Methoden verwendet werden.
Das Gruppieren von Aufgaben nach beliebigen Task-Eigenschaften ist über die Prop groupTasks
möglich:
const [grouping, setGrouping] = useState<GroupConfig | boolean>({
relation_property: 'status',
groups:[
{id: 1, name: "New"},
{id: 2, name: "In Progress"},
{id: 3, name: "Done"}
],
group_id: "key",
group_text: "label"
});
return (
<ReactGantt
ref={ganttRef}
tasks={tasks}
links={links}
groupTasks={grouping}
/>
);
Um die Gruppierung zu deaktivieren, setzen Sie groupTasks
auf false
:
setGrouping(false);
Vertikale Marker können in ReactGantt über die Eigenschaft markers
hinzugefügt werden:
const projectStartMarker = {
id: "marker1",
start_date: new Date(2025, 3, 2),
text: "Project start!",
css: "project-start"
};
const projectEndMarker = {
id: "marker2",
start_date: new Date(2025, 3, 16),
text: "Project end",
css: "project-end"
};
const [markers, setMarkers] = useState<Marker[]>([
projectStartMarker,
projectEndMarker
]);
return (
<div style={ { height: '100%', display: 'flex', flexDirection: 'column' } }>
<ReactGantt
...
markers={markers}
...
/>
</div>
);
Hinweis: Die Eigenschaft text des Marker-Objekts unterstützt sowohl einen HTML-String als auch ein React-Element.
Während die Props von ReactGantt die meisten Konfigurationsanforderungen abdecken, ist für fortgeschrittene Funktionen wie Arbeitszeitberechnungen, gantt.showDate, gantt.unselectTask oder benutzerdefiniertes Zoomen manchmal der direkte Zugriff auf die DHTMLX Gantt API notwendig.
ReactGantt bietet Hooks, die Teile der Gantt-API zugänglich machen. Weitere Details finden Sie im zugehörigen Artikel Verwendung von DHTMLX Gantt Properties in ReactGantt.
Wenn deklarative Props und Hooks nicht ausreichen, kann auf die interne Gantt-Instanz über ref
zugegriffen werden:
import React, { useRef, useEffect } from 'react';
import ReactGantt, { ReactGanttRef } from '@dhx/react-gantt';
export function DirectRefExample({ tasks, links }) {
const ganttRef = useRef<ReactGanttRef>(null);
useEffect(() => {
const gantt = ganttRef.current?.instance;
if (!gantt) return;
// Hier können Sie JEDE Gantt-API-Methode aufrufen
console.log('All tasks:', gantt.getTaskByTime());
gantt.showDate(new Date());
}, []);
return (
<ReactGantt
ref={ganttRef}
tasks={tasks}
links={links}
/>
);
}
Eine vollständige Liste der Methoden finden Sie in der DHTMLX Gantt API Reference.
gantt.parse({ tasks, links })
oder gantt.addTask()
manuell aufrufen, beachten Sie, dass React-Props aus dem Takt geraten können. Andernfalls könnten die nächsten Renderings von React Ihre manuellen Änderungen überschreiben.Da DHTMLX Gantt ein reines Browser-Widget ist, das direkt mit dem DOM interagiert, kann es nicht in Node/SSR-Umgebungen gerendert werden. Daher muss das serverseitige Rendering für alle Routen oder Komponenten, die ReactGantt verwenden, deaktiviert oder verzögert werden.
Für Next.js-Nutzer empfiehlt es sich, die ReactGantt-Komponente dynamisch mit deaktiviertem SSR zu importieren:
import dynamic from 'next/dynamic';
const GanttDemo = dynamic(() => import('../components/GanttDemo'), {
ssr: false
});
export default function GanttPage() {
return (
<div style={ { height: '100vh' } }>
<GanttDemo />
</div>
);
}
Dadurch wird sichergestellt, dass das Gantt-Diagramm nur im Browser geladen wird und Server-Rendering-Fehler vermieden werden.
In Remix empfiehlt es sich, die Gantt-Komponente clientseitig bedingt zu rendern:
import { ClientOnly } from 'remix-utils/client-only';
import ReactGantt from '@dhx/react-gantt';
export default function GanttPage() {
return (
<div style={ { height: '100vh' } }>
<ClientOnly fallback={<p>Loading...</p>}>
{() => <ReactGantt
tasks={[/* ... */]}
links={[/* ... */]}
/>}
</ClientOnly>
</div>
);
}
Dieses Muster verzögert das Rendering, bis die Komponente im Browser geladen ist, und verhindert SSR-Probleme.