
Syncfusion React Gantt Chart
- 488 installs
- 3 repo stars
- Updated July 28, 2026
- syncfusion/react-ui-components-skills
syncfusion-react-gantt-chart is a Syncfusion agent skill that teaches GanttComponent setup, dependencies, and exports for developers who need production-ready React project timeline and resource scheduling UIs.
About
syncfusion-react-gantt-chart is a component-aware agent skill (version 33.1.44) from Syncfusion's react-ui-components-skills pack that teaches AI assistants to implement the React GanttComponent for project scheduling. The SKILL.md maps official APIs for auto/manual/custom scheduling, four dependency types (FS, SS, FF, SF), resource views, critical path, milestones, Excel/PDF export, filtering, virtualization, and RTL localization, and links to 27 feature-specific reference guides such as data-binding, task-constraints, and excel-export. Developers reach for it when building admin portals, construction schedulers, or resource planners that need interactive taskbars, predecessor offsets, and server-side CRUD via DataManager UrlAdaptor. Install with npx skills add syncfusion/react-ui-components-skills --skill syncfusion-react-gantt-chart. The skill expects 14 CSS theme package imports during setup.
- syncfusion-react-gantt-chart
Syncfusion React Gantt Chart by the numbers
- 488 all-time installs (skills.sh)
- +58 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #850 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/react-ui-components-skills --skill syncfusion-react-gantt-chartAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 488 |
|---|---|
| repo stars | ★ 3 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/react-ui-components-skills ↗ |
How do you implement Syncfusion React Gantt charts correctly?
Use syncfusion-react-gantt-chart for development tasks
Who is it for?
React developers integrating Syncfusion GanttComponent who want agent-generated code grounded in official APIs and 27 reference guides.
Skip if: Teams not using Syncfusion licenses who only need a lightweight open-source timeline without enterprise Gantt features.
When should I use this skill?
The developer asks to add a Gantt chart, project timeline, task dependencies, or resource scheduling in a React app with Syncfusion.
What you get
GanttComponent JSX with taskFields mapping, dependency strings, module injections, and themed CSS imports for a schedulable timeline UI.
- GanttComponent React module
- taskFields data mappings
- Export and dependency configuration
By the numbers
- Skill metadata version 33.1.44 from Syncfusion Inc
- Bundles 27 feature reference guides under references/
- Documents 4 task dependency types: FS, SS, FF, and SF
Files
Syncfusion React Gantt Chart
The Syncfusion React Gantt Chart (GanttComponent) is a powerful project management component for visualizing, scheduling, and managing tasks over a timeline. It supports auto/manual scheduling, task dependencies, resource planning, interactive editing, Excel/PDF export, and rich customization.
When to Use This Skill
- Implementing a Gantt chart or project timeline in a React app
- Configuring task scheduling (auto, manual, custom modes)
- Setting up task dependencies (FS, SS, FF, SF) and constraints (ASAP, MSO, FNET, etc.)
- Binding local or remote data to the Gantt (local array, DataManager, UrlAdaptor, Ajax)
- Customizing taskbars, labels, milestones, and baselines
- Configuring resources and resource views (including multi-taskbar)
- Enabling editing (dialog, cell, taskbar drag) with validation rules
- Setting up column resize, reorder, freeze, or column menu
- Configuring WBS codes, tree column icons, or serial number columns
- Setting up filtering, sorting, or selection
- Scrolling to specific dates or controlling scroll position programmatically
- Exporting Gantt to Excel or PDF
- Handling Gantt events and enabling advanced features (undo/redo, virtual scroll, critical path, etc.)
- Globalization, localization, and RTL support
---
Documentation and Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Installation and package dependencies
- Vite / CRA project setup
- CSS theme imports (all 14 required packages)
- Basic
GanttComponentwithtaskFieldsmapping - Module injection guide (all available services)
gridLinesprop ('Both'/'Horizontal'/'Vertical'/'None')- First render walkthrough with minimal example, output summary, and common gotchas
Data Binding
📄 Read: references/data-binding.md
- Local array data binding
- Remote / AJAX data binding with
DataManager - Self-referential flat data
taskFieldsconfiguration (id, name, startDate, endDate, duration, progress, child)- Custom field mapping
- UrlAdaptor for server-side CRUD (
url+batchUrl) - RemoteSaveAdaptor for local rendering with server persistence
- Sending additional parameters to server via
Query.addParams()inloadevent - Binding with
Ajaxclass (manual fetch) - Load child on demand (
loadChildOnDemand={false},hasChildMapping) - Handling HTTP errors with
actionFailureevent
Task Scheduling
📄 Read: references/task-scheduling.md
- Auto, Manual, and Custom scheduling modes (
taskMode) - Unscheduled tasks (duration-only, date-only, none)
- Project start date and end date configuration
- How scheduling mode affects parent task calculation
- Switching between modes dynamically
Task Dependency
📄 Read: references/task-dependency.md
- Dependency types: Finish-to-Start (FS), Start-to-Start (SS), Finish-to-Finish (FF), Start-to-Finish (SF)
- Mapping
taskFields.dependencyin data source (string format:"2FS","2FS+3d","2SS+2h") - Multiple predecessors per task (comma-separated:
"2FS,3FS") - Offset values with units: day, hour, minute (e.g.,
"2FS+3h","2FS-1d") - Drawing dependencies via UI (drag from taskbar connection points)
- Programmatic dependency management (
addPredecessor,removePredecessor,updatePredecessor) - Connector line customization (
connectorLineWidth,connectorLineBackground) - Predecessor Offset Synchronization
autoUpdatePredecessorOffset={true}— recalculate offsets on load to account for calendar rules (weekends, holidays)autoUpdatePredecessorOffset={false}— display original offsets even if not matching rendered lines (visual inconsistency possible)- Offset Updates During Editing
updateOffsetOnTaskbarEdit={true}— auto-recalculate offsets when dragging taskbars (default)updateOffsetOnTaskbarEdit={false}— preserve offsets; manual update required via dependency dialog- Dependency Validation Modes (in
actionBeginevent withrequestType === 'validateLinkedTask') respectLink={true}— revert invalid edits to maintain dependency integrityremoveLink={true}— remove conflicting dependencies to allow editspreserveLinkWithEditing={true}— update offsets to maintain links (default mode)- Validation Dialog — disable all modes to show user-choice dialog (cancel edit, remove link, adjust offset)
- Parent Dependencies —
allowParentDependency={true}allows cross-parent dependencies;falserestricts to siblings only - Show/Hide Dependencies Dynamically — toggle
.e-gantt-dependency-view-containerCSS visibility - Disable Validation —
enablePredecessorValidation={false}disables auto-date validation based on predecessor links
Task Constraints
📄 Read: references/task-constraints.md
- Eight constraint types: ASAP, ALAP, MSO, MFO, SNET, SNLT, FNET, FNLT
- Mapping
taskFields.constraintTypeandtaskFields.constraintDate - How constraints interact with dependencies and
taskMode - Editing constraints via dialog
- Validation and conflict handling
Timeline and Zooming
📄 Read: references/timeline.md
timelineViewModequick presets (Hour,Day,Week,Month,Year)- Top tier and bottom tier independent configuration (
unit,format,count,formatter) - Combining timeline cells using
count(merge multiple units per cell) - Custom timeline cell formatting using
formatter(tier + mode aware) - Timeline view dates with
viewStartDate/viewEndDate(lock visible range) weekStartDay— controls which day starts a week column (0=Sun, 1=Mon…)- Automatic timescale updates using
updateTimescaleView - Dynamic timeline mode change at runtime (update
timelineViewMode+refresh()) - Timeline cell tooltip visibility using
showTooltip - Timeline template customization using
timelineTemplate({ date, value, tier }) - Timeline navigation using
previousTimeSpan()/nextTimeSpan() - Zooming with toolbar (
ZoomIn,ZoomOut,ZoomToFit) - Customizing zoom steps with
zoomingLevels(ZoomTimelineSettings) - Programmatic zoom methods:
zoomIn(),zoomOut(),fitToProject() - Timeline cell width using
timelineUnitSize - Weekend handling:
- Highlight weekends with
highlightWeekends={true} - Hide weekends using
timelineSettings.showWeekend(with noted limitations)
Columns
📄 Read: references/columns.md
- Column types overview (tree column, WBS, custom columns)
- Auto-generated columns (omit
ColumnsDirective) - Column rendering with
valueAccessor(custom display values, concatenation) - Expression columns (calculated values via
valueAccessor) - Display serial numbers via
rowDataBound - Tree column configuration (
treeColumnIndex) - Customize expand/collapse icons (CSS override)
- Customize indentation via
queryCellInfo collapseAllParentTasks— collapse all on loadtaskFields.expandState— per-row initial expand/collapse- Persist expand/collapse state in
localStorage - WBS (Work Breakdown Structure) column
enableWBS+enableAutoWbsUpdateAPIs- Manual WBS with
taskFields.wbsPredecessor - Column headers, format, and alignment
headerTemplatefor custom header rendering- Header text wrapping (
treeGrid.textWrapSettings.wrapMode) - Dynamic header text update (
getColumnByField+refreshHeader) - Column templates (custom cell rendering) and
editTemplate - Column spanning and frozen columns (
frozenColumns, injectFreeze)
Column Interactions
📄 Read: references/column-interactions.md
- Column resizing (
allowResizing, injectResize) - Restrict with
minWidth/maxWidthper column - Prevent resize per column (
allowResizing={false}) - Resize modes: Normal vs Auto (
resizeSettings.mode) - Programmatic resize (
reorderColumns,refreshColumns) - Resize events:
resizeStart,resizing,resizeStop - Column reorder (
allowReordering, injectReorder) - Disable reorder per column (
allowReordering={false}) - Programmatic reorder:
reorderColumns,reorderColumnByIndex,reorderColumnByTargetIndex - Reorder drag events:
columnDragStart,columnDrag,columnDrop - Column menu (
showColumnMenu, injectColumnMenu) - Built-in items: SortAscending, SortDescending, AutoFit, AutoFitAll, ColumnChooser, Filter
- Custom column menu items +
columnMenuClickevent - Disable per column (
showColumnMenu={false}) - Toggling column visibility (show/hide columns)
Rows
📄 Read: references/rows.md
- Row drag-and-drop (reorder and reparent tasks)
- Indent and outdent (change task hierarchy)
- Row spanning via
queryCellInfoevent QueryCellInfoEventArgswithrowSpan,colSpan,cell,data,column- Horizontal spanning (
colSpan) and vertical spanning (rowSpan) - Row height configuration
- Row styling via
rowDataBound - Expand and collapse rows programmatically
Managing Tasks
📄 Read: references/managing-tasks.md
- Adding new tasks (toolbar, context menu, programmatic
addRecord()) - Editing tasks — dialog edit (
mode: 'Dialog'), cell edit (mode: 'Auto'), taskbar drag - Deleting tasks (toolbar,
deleteRow(),showDeleteConfirmDialog) - Taskbar drag editing: move, resize right edge, progress handle, dependency drawing
- Splitting and merging tasks (
taskFields.segments,splitTask(),mergeTask()) - Cell edit types (
editType):stringedit,numericedit,datepickeredit,datetimepickeredit,dropdownedit,booleanedit edit.paramsfor min/max/step/format/dataSource per column- Customizing add/edit dialog fields (
addDialogFields,editDialogFields) - Server-side CRUD with
UrlAdaptor(url+batchUrl) - Server receives
added,changed,deletedarrays in the batch request body - Column-level validation rules (
validationRulesonColumnDirective) - Built-in rules:
required,min,max,minLength,date,regex - Custom validator functions (
[fn, 'message']callback pattern)
Filtering and Searching
📄 Read: references/filtering-searching.md
- Basic column filtering (
allowFiltering={true}, injectFilter) - Filter hierarchy modes:
Parent,Child,Both,None - Filter menu (string, number, date operators) and Excel-like checklist filter
- Toolbar search box (
searchSettings:fields,operator,ignoreCase) - Initial filter state on load (
filterSettings.columnsarray) - Programmatic filter API:
filterByColumn(),clearFiltering() - Custom multi-column filter predicates
Sorting
📄 Read: references/sorting.md
- Single-column and multi-column sorting
- Initial sort state (
sortSettings) - Custom sort comparers
- Programmatic sorting API
Selection
📄 Read: references/selection.md
- Row selection modes (single, multiple)
- Cell selection
- Keyboard-based selection
- Programmatic selection API (
selectRow,selectCell)
Taskbar, Labels, and Markers
📄 Read: references/taskbar-labels-markers.md
- Taskbar template (custom rendering)
- Parent taskbar and milestone customization
- Tooltip settings and templates (
tooltipSettingsfor taskbar, connector line, baseline, timeline) - Disable/hide tooltips for specific UI targets using
beforeTooltipRender - Left, right, and task label configuration
- Data markers / indicators
- Event markers (deadlines, holidays on timeline)
- Baseline rendering
- Critical path highlighting
Resources
📄 Read: references/resources.md
- Defining resource collection (
resourcesproperty) - Assigning resources to tasks (
taskFields.resourceInfo) — single or multiple with custom units - Resource view layout (
viewType='ResourceView', resource groups) - Multi-taskbar (
enableMultiTaskbar={true}) — multiple taskbars per collapsed resource row - Over-allocation highlighting (
showOverAllocatedTasks={true}) - Resource unit and work fields (
resourceFields.unit,resourceFields.group) - Custom resource taskbar styling via
queryTaskbarInfo
Toolbar and Context Menu
📄 Read: references/toolbar-context-menu.md
- Built-in toolbar items:
Add,Edit,Delete,Update,Cancel,Search,ExpandAll,CollapseAll,Indent,Outdent,ZoomIn,ZoomOut,ZoomToFit,CriticalPath,ExcelExport,CsvExport,PdfExport,ColumnChooser,Undo,Redo,SplitTask,MergeTask - Required services per toolbar item (e.g.
Undo/Redo→UndoRedo,SplitTask→Edit) - Custom toolbar buttons (
ItemModel) andtoolbarClickhandler - Default context menu items including
SplitTask,MergeTask,Convert,DeleteDependency - Custom context menu items,
contextMenuClick,contextMenuOpen(args.hideItems) - Enabling/disabling toolbar items dynamically
Excel Export
📄 Read: references/excel-export.md
- Basic Excel export
- Export properties and customization
- Exporting multiple Gantt charts to a single Excel workbook
- Custom data source export
PDF Export
📄 Read: references/pdf-export.md
- Basic PDF export
- Customizing PDF (fonts, columns, styles)
- Header and footer configuration
- Page settings (orientation, size)
Holidays, Working Time & Timezone
📄 Read: references/holidays-working-time.md
- Defining holidays (
holidaysprop,HolidayModel,DayMarkersservice) - Work week configuration (
workWeek,includeWeekend) - Daily working time (
dayWorkingTime) - Effort-based scheduling (
taskFields.work,workUnit, task types: FixedWork / FixedDuration / FixedUnit) - Timezone support (
timezoneprop, IANA timezone strings)
Splitter
📄 Read: references/splitter.md
- Position-based split (
splitterSettings.position— percentage or pixels) - Column-based split (
splitterSettings.columnIndex) - View mode (
splitterSettings.view— Default / Grid / Chart) - Programmatic resize (
setSplitterPosition()) - Splitter events (
splitterResizeStart,splitterResizing,splitterResized)
Scrolling
📄 Read: references/scrolling.md
- Setting
widthandheightfor scrollbar control - Responsive container (
width='100%',height='100%'with CSS parent height) - Scroll to a date (
scrollToDate('MM/dd/yyyy')) - Set vertical scroll position (
ganttChartModule.scrollObject.setScrollTop(px))
Virtual Scroll
📄 Read: references/virtual-scroll.md
- Row virtualization (
enableVirtualization={true},VirtualScrollservice) - Timeline virtualization (
enableTimelineVirtualization={true}, requiresprojectStartDate/projectEndDate) - Combining row + timeline virtualization
- Accessing filtered record count with virtual scroll (
treeGrid.filterModule.filteredResult) - Virtual scroll limitations (browser memory, cell selection, pixel height, expand state)
Undo / Redo
📄 Read: references/undo-redo.md
- Enable undo/redo (
enableUndoRedo,UndoRedoservice) undoRedoActions— full list of trackable actions and required servicesundoRedoStepsCount— history stack size- Toolbar
'Undo'/'Redo'buttons andCtrl+Z/Ctrl+Ykeyboard shortcuts - Programmatic
undo()/redo()via component ref
State Persistence, Immutable Mode & Loading
📄 Read: references/state-persistence.md
- State persistence (
enablePersistence={true}, uniqueidprop, localStorage) - Resetting persisted state (change
idor clear localStorage entry) - Immutable mode (
enableImmutableMode={true}, requiresisPrimaryKey={true}) - Loading animation (
loadingIndicator.indicatorType:'Spinner'or'Shimmer') - Manual spinner control (
showSpinner()/hideSpinner())
Localization and RTL
📄 Read: references/localization-rtl.md
- Setting locale (
localeprop) - Loading custom translations (
L10n.load()with'gantt'namespace keys) - Gantt locale key reference (all translatable UI strings)
- RTL support (
enableRtl={true}) - Column date formatting (
format='yMd',format='dd/MM/yyyy') - Column number formatting (
format='C2',format='N2',format='P0')
Programmatic Methods
📄 Read: references/gantt-methods.md
- Column utilities:
autoFitColumns() - Edit control:
cancelEdit(),openAddDialog(),openEditDialog() - Task management:
deleteRecord(),convertToMilestone(),changeTaskMode(),updateRecordByID(),updateRecordByIndex(),updateTaskId(),updateDataSource(),updateProjectDates() - Toolbar control:
enableItems() - Expand/Collapse:
expandByIndex(),collapseByIndex() - Undo/Redo stacks:
clearUndoCollection(),clearRedoCollection(),getUndoActions(),getRedoActions() - Data retrieval:
getCurrentViewData(),getRecordByID(),getTaskByUniqueID(),getTaskInfo(),getTaskbarHeight(),getExpandedRecords(),getGanttColumns(),getGridColumns() - Formatting helpers:
getDurationString(),getWorkString() - DOM access:
getRowByID(),getRowByIndex() - Sorting:
removeSortColumn() - Selection:
selectCells() - Row reorder:
reorderRows() - Scrolling:
scrollToTask(),updateChartScrollOffset()
Events
📄 Read: references/events.md
- Lifecycle events (
load,created,dataBound) - Task editing events (
actionBegin,actionComplete) withrequestTypereference table - Taskbar events:
taskbarEditing,taskbarEdited,taskbarClick - Row expand/collapse events:
expanding,collapsing,expanded,collapsed - Selection events:
rowSelected,rowDeselected,cellSelected,cellDeselected - Export events:
excelExportComplete,pdfExportComplete - Column interaction events:
resizeStart,resizing,resizeStop,columnDragStart,columnDrag,columnDrop - Event cancellation patterns (
args.cancel = true) - Complete all-events quick reference table (30+ events)
---
Quick Start Example
import * as React from 'react';
import { GanttComponent, ColumnsDirective, ColumnDirective, Inject, Edit, Toolbar, Selection } from '@syncfusion/ej2-react-gantt';
import { TaskFieldsModel, EditSettingsModel } from '@syncfusion/ej2-gantt';
// In App.css:
// @import "../node_modules/@syncfusion/ej2-gantt/styles/tailwind3.css";
// (plus base, buttons, calendars, dropdowns, grids, inputs, layouts,
// lists, navigations, notifications, popups, richtexteditor, treegrid)
const data = [
{
TaskID: 1, TaskName: 'Project Initiation',
StartDate: new Date('04/02/2024'), EndDate: new Date('04/21/2024'),
subtasks: [
{ TaskID: 2, TaskName: 'Identify Site Location', StartDate: new Date('04/02/2024'), Duration: 4, Progress: 50 },
{ TaskID: 3, TaskName: 'Perform Soil Test', StartDate: new Date('04/02/2024'), Duration: 4, Progress: 50, Predecessor: '2' },
{ TaskID: 4, TaskName: 'Soil Test Approval', StartDate: new Date('04/08/2024'), Duration: 0, Predecessor: '3FS', Progress: 50 },
]
}
];
const taskFields: TaskFieldsModel = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
dependency: 'Predecessor',
child: 'subtasks',
};
const editSettings: EditSettingsModel = { allowEditing: true, allowAdding: true, allowDeleting: true, allowTaskbarEditing: true };
function App() {
return (
<GanttComponent
dataSource={data}
taskFields={taskFields}
editSettings={editSettings}
toolbar={['Add', 'Edit', 'Delete', 'Cancel', 'Update', 'Search', 'ZoomIn', 'ZoomOut', 'ZoomToFit']}
height="450px"
>
<ColumnsDirective>
<ColumnDirective field="TaskID" width="80" />
<ColumnDirective field="TaskName" headerText="Task Name" width="250" />
<ColumnDirective field="StartDate" />
<ColumnDirective field="Duration" />
<ColumnDirective field="Progress" />
</ColumnsDirective>
<Inject services={[Edit, Toolbar, Selection]} />
</GanttComponent>
);
}
export default App;---
Common Patterns
Enable Editing with Dialog
<GanttComponent
editSettings={{ allowEditing: true, mode: 'Dialog' }}
>
<Inject services={[Edit]} />
</GanttComponent>Enable Cell Editing
<GanttComponent
editSettings={{ allowEditing: true, mode: 'Auto' }}
>
<Inject services={[Edit]} />
</GanttComponent>Programmatically Add a Task
const ganttRef = React.useRef<GanttComponent>(null);
const addTask = () => {
ganttRef.current?.addRecord({ TaskID: 10, TaskName: 'New Task', Duration: 3 }, 'Below', 1);
};Export to Excel
<GanttComponent toolbar={['ExcelExport']} allowExcelExport={true}>
<Inject services={[ExcelExport, Toolbar]} />
</GanttComponent>Export to PDF
<GanttComponent toolbar={['PdfExport']} allowPdfExport={true}>
<Inject services={[PdfExport, Toolbar]} />
</GanttComponent>Column-Level Validation Rules
<ColumnsDirective>
<ColumnDirective field="TaskName" validationRules={{ required: true }} />
<ColumnDirective field="Duration" validationRules={{ min: 1, max: 100 }} />
</ColumnsDirective>Scroll to Date Programmatically
const ganttRef = React.useRef<GanttComponent>(null);
ganttRef.current?.scrollToDate('04/28/2024');Column Resize with Min/Max Width
<ColumnDirective field="TaskName" minWidth="100" maxWidth="400" allowResizing={true} />Reorder Columns Programmatically
const ganttRef = React.useRef<GanttComponent>(null);
// By field names:
(ganttRef.current as any).treeGrid.reorderColumns(['Duration', 'Progress'], 'TaskName');
// By index:
(ganttRef.current as any).treeGrid.reorderColumnByIndex(2, 0);Persist Tree Expand/Collapse State
// In taskFields — map a boolean field that controls initial expand state:
const taskFields = { ..., expandState: 'isExpand' };
// Or collapse all on load:
<GanttComponent collapseAllParentTasks={true} .../>---
Key Module Injections
| Feature | Service to Inject |
|---|---|
| Editing (add/edit/delete/taskbar drag) | Edit |
| Filtering | Filter |
| Sorting | Sort |
| Toolbar | Toolbar |
| Selection | Selection |
| Day Markers / Holidays / Event Markers | DayMarkers |
| Critical Path | CriticalPath |
| Excel / CSV Export | ExcelExport |
| PDF Export | PdfExport |
| Undo / Redo | UndoRedo |
| Column Menu | ColumnMenu |
| Context Menu | ContextMenu |
| Virtual Scrolling | VirtualScroll |
| Row Drag and Drop | RowDD |
| Column Reordering | Reorder |
| Frozen Columns | Freeze |
| Column Resizing | Resize |
Usage pattern:
import { GanttComponent, Inject, Edit, Filter, Sort, Toolbar, Selection } from '@syncfusion/ej2-react-gantt';
<GanttComponent ...>
<Inject services={[Edit, Filter, Sort, Toolbar, Selection]} />
</GanttComponent>---
Key Props Reference
| Prop | Type | Purpose |
|---|---|---|
dataSource | object[] / DataManager | Task data (local or remote via DataManager) |
taskFields | TaskFieldsModel | Field mapping for task properties |
editSettings | EditSettingsModel | Enable add/edit/delete/taskbar editing |
toolbar | string[] | Toolbar items to render |
taskMode | Auto / Manual / Custom | Scheduling mode |
resources | object[] | Resource data collection |
resourceFields | ResourceFieldsModel | Resource field mapping |
allowSorting | boolean | Enable sorting |
allowFiltering | boolean | Enable filtering |
allowReordering | boolean | Enable column reorder |
allowResizing | boolean | Enable column resize |
allowExcelExport | boolean | Enable Excel export |
allowPdfExport | boolean | Enable PDF export |
undoRedoActions | string[] | Actions tracked by undo/redo |
timelineSettings | TimelineSettingsModel | Timeline tiers and unit config |
splitterSettings | SplitterSettingsModel | Grid/chart panel ratio |
highlightWeekends | boolean | Shade weekend columns |
holidays | HolidayModel[] | Mark holiday dates |
eventMarkers | EventMarkerModel[] | Vertical markers on timeline |
treeColumnIndex | number | Column index showing expand/collapse icons |
collapseAllParentTasks | boolean | Collapse all parent rows on initial load |
enableWBS | boolean | Auto-generate WBS codes from task hierarchy |
enableAutoWbsUpdate | boolean | Recalculate WBS codes after CRUD/sort/DnD |
frozenColumns | number | Number of left-pinned (frozen) columns |
enableMultiTaskbar | boolean | Show multiple taskbars per resource row |
allowTaskbarOverlap | boolean | Controls whether multiple taskbars overlap (true, default) or stack with extended row height (false) in resource rows |
viewType | string | 'ProjectView' or 'ResourceView' |
showColumnMenu | boolean | Show column-level menu (sort, filter, autofit) |
columnMenuItems | string[] | Restrict which column menu items are shown |
showColumnChooser | boolean | Enable column chooser dialog via toolbar ColumnChooser item |
allowSelection | boolean | Enable row/cell selection |
selectionSettings | SelectionSettingsModel | Selection mode (Row/Cell), type (Single/Multiple) |
gridLines | string | Grid lines style: 'Both' \ |
renderBaseline | boolean | Show baseline bars alongside actual taskbars |
baselineColor | string | CSS color for baseline bars |
enableCriticalPath | boolean | Highlight critical path tasks and connectors |
enableRtl | boolean | Enable right-to-left layout |
locale | string | Locale code (e.g. 'fr', 'de', 'ar') |
allowRowDragAndDrop | boolean | Enable row drag-and-drop reordering/reparenting |
enableImmutableMode | boolean | Only re-render changed rows on data updates (requires isPrimaryKey) |
enablePersistence | boolean | Persist UI state (sort, filter, column widths) to localStorage |
loadingIndicator | object | Loading animation type: `{ indicatorType: 'Spinner' \ |
workWeek | string[] | Working days, e.g. ['Monday','Tuesday','Wednesday','Thursday','Friday'] |
includeWeekend | boolean | Count weekends as working days for duration calculation |
timezone | string | IANA timezone for the Gantt (e.g. 'America/New_York') |
showOverAllocatedTasks | boolean | Highlight over-allocated resources in ResourceView |
enableUndoRedo | boolean | Enable undo/redo tracking (inject UndoRedo service) |
undoRedoStepsCount | number | Maximum undo/redo history steps (default: 10) |
enableVirtualization | boolean | Row virtualization for large data sets |
enableTimelineVirtualization | boolean | Timeline (horizontal) virtualization |
rowHeight | number | Uniform row height in pixels |
taskbarHeight | number | Height of taskbars in pixels (must be ≤ rowHeight) |
connectorLineWidth | number | Width of dependency connector lines |
connectorLineBackground | string | Color of dependency connector lines |
Column Interactions in Syncfusion React Gantt Chart
Table of Contents
- Column Resizing
- Restrict Resizing with Min/Max Width
- Prevent Resizing per Column
- Column Resizing Modes
- Resize Columns Programmatically
- Column Resize Events
- Column Reorder (Drag-to-Reorder)
- Disable Reorder per Column
- Reorder Columns Programmatically
- Column Reorder Events
- Column Menu
- Column Visibility Toggle (Column Chooser)
---
Column Resizing
Allow users to drag column borders to resize:
import { Resize } from '@syncfusion/ej2-react-gantt';
<GanttComponent allowResizing={true} ...>
<Inject services={[Resize]} />
</GanttComponent>Disable resize per column:
<ColumnDirective field="TaskID" allowResizing={false} width="80" />In RTL mode, drag the left edge of the header cell to resize.
---
Restrict Resizing with Min/Max Width
Set minWidth and maxWidth on a column to clamp its resize range:
<GanttComponent allowResizing={true} ...>
<ColumnsDirective>
<ColumnDirective field="TaskID" width="100" />
<ColumnDirective field="TaskName" headerText="Task Name" minWidth="200" width="250" maxWidth="300" />
<ColumnDirective field="Duration" minWidth="100" maxWidth="200" />
<ColumnDirective field="Progress" />
</ColumnsDirective>
<Inject services={[Resize]} />
</GanttComponent>---
Prevent Resizing per Column
Set allowResizing={false} on a specific ColumnDirective to lock it:
<GanttComponent allowResizing={true} ...>
<ColumnsDirective>
<ColumnDirective field="TaskID" width="100" allowResizing={false} />
<ColumnDirective field="TaskName" width="250" />
</ColumnsDirective>
<Inject services={[Resize]} />
</GanttComponent>You can also cancel resizing programmatically in the resizeStart event by setting args.cancel = true.
---
Column Resizing Modes
Two modes control how remaining space is redistributed during resize:
| Mode | Behavior |
|---|---|
Normal | Columns keep defined widths; extra space or scrollbar appears as needed |
Auto | Columns auto-fill available space; no leftover empty space |
Set the mode via the underlying TreeGrid's resizeSettings:
function load() {
ganttRef.current!.treeGrid.grid.resizeSettings.mode = 'Auto'; // or 'Normal'
}
<GanttComponent load={load} allowResizing={true} ref={ganttRef} ...>
<Inject services={[Resize]} />
</GanttComponent>WhenautoFit={true}is set on the grid, columns auto-size to content. InNormalmode leftover space is preserved; inAutomode it is consumed.
---
Resize Columns Programmatically
Access the column by field name and update its width, then call refreshColumns():
const ganttRef = React.useRef<GanttComponent>(null);
function resizeTaskName(newWidth: string) {
const col = (ganttRef.current as any).treeGrid.grid.getColumnByField('TaskName');
col.width = newWidth;
(ganttRef.current as any).treeGrid.grid.refreshColumns();
}
<GanttComponent ref={ganttRef} allowResizing={true} ...>
<Inject services={[Resize]} />
</GanttComponent>---
Column Resize Events
Three events fire during a resize operation:
| Event | Trigger | Cancel support |
|---|---|---|
resizeStart | User starts dragging | args.cancel = true to prevent |
resizing | Fires repeatedly while dragging | — |
resizeStop | User releases the drag | — |
function resizeStart(args: any) {
if (args.column.field === 'TaskID') {
args.cancel = true; // prevent resizing TaskID
}
}
function resizeStop(args: any) {
// Apply custom style after resize
const header = (ganttRef.current as any).treeGrid.grid
.getColumnHeaderByField(args.column.field);
header.classList.add('resized-col');
}
function resizing(args: any) {
console.log('Resizing column:', args.column.field);
}
<GanttComponent
allowResizing={true}
resizeStart={resizeStart}
resizing={resizing}
resizeStop={resizeStop}
ref={ganttRef}
...
>
<Inject services={[Resize]} />
</GanttComponent>---
Column Reorder (Drag-to-Reorder)
Allow users to drag column headers to change order:
import { Reorder } from '@syncfusion/ej2-react-gantt';
<GanttComponent allowReordering={true} ...>
<Inject services={[Reorder]} />
</GanttComponent>After reordering, any logic that depends on column position should be updated to reflect the new order.
---
Disable Reorder per Column
Set allowReordering={false} on a specific ColumnDirective:
<GanttComponent allowReordering={true} ...>
<ColumnsDirective>
<ColumnDirective field="TaskID" width="90" />
<ColumnDirective field="TaskName" width="290" allowReordering={false} /> {/* locked */}
<ColumnDirective field="Duration" width="90" />
</ColumnsDirective>
<Inject services={[Reorder]} />
</GanttComponent>---
Reorder Columns Programmatically
By Field Name (reorderColumns)
Move one or more columns before a target column using field names:
const ganttRef = React.useRef<GanttComponent>(null);
// Single column: move TaskName before TaskID
ganttRef.current?.reorderColumns('TaskName', 'TaskID');
// Multiple columns: move TaskName, StartDate, Duration before TaskID
ganttRef.current?.reorderColumns(['TaskName', 'StartDate', 'Duration'], 'TaskID');By Column Index (reorderColumnByIndex)
Move the column at fromIndex to toIndex via the underlying grid:
// Move column at index 1 to index 3
(ganttRef.current as any).treeGrid.grid.reorderColumnByIndex(1, 3);By Target Index (reorderColumnByTargetIndex)
Move one or more columns (by field name) to a specific target index:
// Move single column TaskID to index 3
(ganttRef.current as any).treeGrid.grid.reorderColumnByTargetIndex('TaskID', 3);
// Move multiple columns to index 3
(ganttRef.current as any).treeGrid.grid.reorderColumnByTargetIndex(['TaskID', 'TaskName'], 3);---
Column Reorder Events
Three events fire during drag-based column reorder:
| Event | Trigger |
|---|---|
columnDragStart | User starts dragging a column header |
columnDrag | Fires continuously while dragging |
columnDrop | User drops the column at a new position |
import { ColumnDragEventArgs } from '@syncfusion/ej2-react-gantt';
function columnDragStart(args: ColumnDragEventArgs) {
// Example: change header text on drag start
if (args.column.field === 'TaskName') {
args.column.headerText = 'Project Task';
}
}
function columnDrag(args: ColumnDragEventArgs) {
// Example: prevent dragging the Duration column
if (args.column.field === 'Duration') {
args.column.allowReordering = false;
}
}
function columnDrop(args: ColumnDragEventArgs) {
// Example: cancel drop for TaskID column
if (args.column.field === 'TaskID') {
args.column.allowReordering = false;
}
}
<GanttComponent
allowReordering={true}
columnDragStart={columnDragStart}
columnDrag={columnDrag}
columnDrop={columnDrop}
...
>
<Inject services={[Reorder]} />
</GanttComponent>---
Column Menu
The column menu shows a dropdown on each column header with options (sort, filter, column chooser, etc.):
import { ColumnMenu, Sort, Filter } from '@syncfusion/ej2-react-gantt';
<GanttComponent showColumnMenu={true} allowSorting={true} allowFiltering={true} ...>
<Inject services={[ColumnMenu, Sort, Filter]} />
</GanttComponent>Customize Column Menu Items
const columnMenuItems = ['SortAscending', 'SortDescending', 'ColumnChooser', 'Filter'];
<GanttComponent
showColumnMenu={true}
columnMenuItems={columnMenuItems}
...
>
<Inject services={[ColumnMenu, Sort, Filter]} />
</GanttComponent>Disable column menu per column:
<ColumnDirective field="TaskID" showColumnMenu={false} />---
Column Visibility Toggle (Column Chooser)
Let users show/hide columns via a column chooser dialog:
<GanttComponent showColumnChooser={true} toolbar={['ColumnChooser']} ...>
<Inject services={[Toolbar, ColumnMenu]} />
</GanttComponent>Programmatic show/hide:
ganttRef.current?.showColumn(['Notes', 'Resources']);
ganttRef.current?.hideColumn(['Notes']);Columns in Syncfusion React Gantt Chart
Table of Contents
- Column Types
- Defining Columns
- Auto-Generated Columns
- Column Rendering with valueAccessor
- Expression Column
- Display Serial Numbers
- Tree Column
- WBS (Work Breakdown Structure) Column
- Column Headers and Formatting
- Column Templates (Custom Cell Rendering)
- Column Spanning
- Frozen (Pinned) Columns
- Showing and Hiding Columns
---
Column Types
Each column can have an explicit type for correct rendering and formatting:
| Type | Use Case |
|---|---|
string | Default; text values |
number | Numeric values with optional format |
boolean | Renders as checkbox |
date | Date values |
datetime | Date + time values |
checkbox | Checkbox selection column |
<ColumnDirective field="TaskID" type="number" width="80" />
<ColumnDirective field="TaskName" type="string" width="250" />
<ColumnDirective field="StartDate" type="date" format="yMd" />
<ColumnDirective field="Verified" type="boolean" width="100" />
<ColumnDirective type="checkbox" width="50" /> {/* selection column */}---
Defining Columns
Use ColumnsDirective + ColumnDirective to declare columns declaratively:
import {
GanttComponent, ColumnsDirective, ColumnDirective
} from '@syncfusion/ej2-react-gantt';
function App() {
return (
<GanttComponent dataSource={data} taskFields={taskFields} height="450px">
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="ID" width="80" textAlign="Right" />
<ColumnDirective field="TaskName" headerText="Task Name" width="250" clipMode="EllipsisWithTooltip" />
<ColumnDirective field="StartDate" headerText="Start" width="130" format="yMd" />
<ColumnDirective field="Duration" headerText="Duration" width="100" />
<ColumnDirective field="Progress" headerText="Progress" width="100" />
</ColumnsDirective>
</GanttComponent>
);
}Key ColumnDirective Props
| Prop | Type | Purpose |
|---|---|---|
field | string | Maps to data source field |
headerText | string | Column header label |
width | string / number | Column width (px or %) |
textAlign | Left / Right / Center | Cell alignment |
format | string | Date/number format (e.g., "yMd", "C2") |
type | string | Data type |
visible | boolean | Show/hide column |
allowEditing | boolean | Enable cell editing for this column |
isPrimaryKey | boolean | Mark as primary key |
clipMode | Clip / Ellipsis / EllipsisWithTooltip | Overflow behavior |
template | ReactElement | Custom cell renderer |
---
Auto-Generated Columns
If ColumnsDirective is omitted (or the columns prop is empty/undefined), the Gantt automatically creates one column for every field in the data source:
// No <ColumnsDirective> → all data source fields become columns automatically
<GanttComponent
dataSource={data}
taskFields={taskFields}
treeColumnIndex={1}
height="450px"
/>This is useful for quick prototyping. For production use, define columns explicitly for full control over order, width, and formatting.
---
Column Rendering with valueAccessor
The valueAccessor property accepts a (field, data) => value function that returns a custom display string for a cell — without affecting the underlying data:
import { getValue } from '@syncfusion/ej2-base';
// Append "%" to Progress values
const percentageFormatter = (field: string, data: any) => `${data[field]}%`;
// Combine TaskName with TaskID
const concatenateFields = (field: string, data: any) =>
`${data[field]} - ${getValue('TaskID', data)}`;
<GanttComponent dataSource={data} taskFields={taskFields} height="450px">
<ColumnsDirective>
<ColumnDirective field="TaskID" />
<ColumnDirective field="TaskName" width="180" valueAccessor={concatenateFields} />
<ColumnDirective field="StartDate" width="180" />
<ColumnDirective field="Duration" width="100" />
<ColumnDirective field="Progress" width="120" valueAccessor={percentageFormatter} />
</ColumnsDirective>
</GanttComponent>Note: Columns with custom valueAccessor display computed values — sorting and filtering operate on the raw field value, not the formatted string.Display Array-Type Column Values
When a field contains an array of objects, use valueAccessor to flatten or join them:
const resourceNames = (field: string, data: any) =>
Array.isArray(data[field])
? data[field].map((r: any) => r.resourceName).join(', ')
: '';
<ColumnDirective field="resources" headerText="Resources" valueAccessor={resourceNames} />---
Expression Column
Calculate a derived value from multiple fields using valueAccessor:
// Total Price = Units × Unit Price
const totalPrice = (field: string, data: any) =>
Number(data.Units) * Number(data.UnitPrice);
<GanttComponent dataSource={data} taskFields={taskFields} height="450px">
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width="100" />
<ColumnDirective field="TaskName" headerText="Task Name" width="250" />
<ColumnDirective field="Units" headerText="Units" width="100" textAlign="Right" />
<ColumnDirective field="UnitPrice" headerText="Unit Price" width="120" textAlign="Right" />
<ColumnDirective
field="TotalPrice"
headerText="Total Price"
width="120"
format="c2"
type="number"
textAlign="Right"
valueAccessor={totalPrice}
/>
</ColumnsDirective>
</GanttComponent>Sorting and filtering are not supported on expression columns because the displayed value is computed client-side.
---
Display Serial Numbers
Use rowDataBound to write sequential row numbers into a dedicated column cell:
function rowDataBound(args: any) {
const row = args.row;
if (row) {
const rowIndex = parseInt(row.getAttribute('aria-rowindex') || '0', 10);
const cells = row.querySelectorAll('.e-rowcell');
if (cells.length > 0) {
cells[0].textContent = rowIndex.toString();
}
}
}
<GanttComponent
dataSource={data}
taskFields={taskFields}
treeColumnIndex={1}
rowDataBound={rowDataBound}
height="450px"
>
<ColumnsDirective>
<ColumnDirective field="SNo" headerText="S.No" width="80" />
<ColumnDirective field="TaskName" headerText="Task Name" width="250" />
<ColumnDirective field="StartDate" />
<ColumnDirective field="Duration" />
<ColumnDirective field="Progress" />
</ColumnsDirective>
</GanttComponent>Serial number columns do not support sorting or filtering because the value is injected via the DOM after data binding.
---
Tree Column
The tree column shows the hierarchical expand/collapse icons. By default it's the first column (treeColumnIndex={0}). Change it with treeColumnIndex:
<GanttComponent
treeColumnIndex={1} // TaskName column (index 1) becomes the tree column
...
/>Customize Expand/Collapse Icons
Override the default CSS to use custom icons on the tree column:
.e-gantt .e-grid .e-treegridexpand::before {
content: "\2795"; /* ➕ */
}
.e-gantt .e-grid .e-treegridcollapse::before {
content: "\2796"; /* ➖ */
}Customize Indentation of Tree Column Text
Use queryCellInfo to apply custom CSS classes for indentation styling on tree column cells:
const queryCellInfo = (args: any): void => {
const rowData = args.data;
const columnIndex = args.column.index;
const treeColumnIndex = ganttRef.current?.treeColumnIndex;
// Add custom indentation class only to leaf rows in the tree column
if (!rowData.hasChildRecords && columnIndex === treeColumnIndex) {
args.cell.classList.add('custom-indent');
}
};
<GanttComponent queryCellInfo={queryCellInfo} treeColumnIndex={1} ...>Render Parent Rows in Collapsed State
Collapse all parent rows on initial load using collapseAllParentTasks:
<GanttComponent
collapseAllParentTasks={true}
...
/>Retain Expand/Collapse State per Row
Use taskFields.expandState to map a data field that controls whether each row starts expanded or collapsed:
const taskFields = {
id: 'TaskID',
name: 'TaskName',
// ...
expandState: 'isExpand', // boolean field in your data — true = expanded, false = collapsed
parentID: 'ParentID',
};Data example:
{ TaskID: 1, TaskName: 'Planning', isExpand: false } // starts collapsed
{ TaskID: 2, TaskName: 'Design', isExpand: true } // starts expandedPersist Expand/Collapse State via localStorage
Use the collapsed/expanded events + dataBound to save and restore row states across page refreshes:
import { GanttComponent, CollapsingEventArgs } from '@syncfusion/ej2-react-gantt';
const ganttRef = React.useRef<GanttComponent>(null);
const collapsingData: number[] = [];
const onDataBound = (): void => {
if (ganttRef.current?.treeGrid.initialRender && window.localStorage) {
const stored = JSON.parse(localStorage.getItem('collapsingData') || '[]');
stored.forEach((key: number) => {
ganttRef.current?.treeGrid.collapseByKey(key);
});
}
};
const onCollapsed = (args: CollapsingEventArgs): void => {
collapsingData.push((args.data as any).TaskID);
localStorage.setItem('collapsingData', JSON.stringify(collapsingData));
};
const onExpanded = (args: CollapsingEventArgs): void => {
const i = collapsingData.findIndex(id => id === (args.data as any).TaskID);
if (i !== -1) {
collapsingData.splice(i, 1);
localStorage.setItem('collapsingData', JSON.stringify(collapsingData));
}
};
<GanttComponent
ref={ganttRef}
dataBound={onDataBound}
collapsed={onCollapsed}
expanded={onExpanded}
...
/>Events used:
| Event | Trigger | Use |
|---|---|---|
dataBound | After data renders | Restore collapsed state from storage |
collapsed | Row collapses | Save collapsed row ID to storage |
expanded | Row expands | Remove row ID from storage |
---
WBS (Work Breakdown Structure) Column
WBS displays hierarchical numbering (e.g., 1, 1.1, 1.1.1) to identify tasks.
Enable WBS with enableWBS
Set enableWBS={true} to automatically generate unique WBS codes for each task based on hierarchy, and enableAutoWbsUpdate={true} to keep codes accurate after operations like sorting, editing, or drag-and-drop:
import {
GanttComponent, ColumnsDirective, ColumnDirective, Inject,
Selection, Toolbar, Edit, Filter, Sort, DayMarkers
} from '@syncfusion/ej2-react-gantt';
const taskFields = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
dependency: 'Predecessor',
parentID: 'ParentID',
};
function App() {
return (
<GanttComponent
dataSource={WBSData}
taskFields={taskFields}
enableWBS={true} // auto-generate WBS codes from hierarchy
enableAutoWbsUpdate={true} // recalculate codes after edits/sorting/DnD
treeColumnIndex={2}
height="550px"
>
<ColumnsDirective>
<ColumnDirective field="TaskID" visible={false} />
<ColumnDirective field="WBSCode" width="150px" />
<ColumnDirective field="TaskName" headerText="Task Name" width="260px" />
<ColumnDirective field="StartDate" headerText="Start Date" width="140px" />
<ColumnDirective field="WBSPredecessor" headerText="WBS Predecessor" width="190px" />
<ColumnDirective field="Duration" headerText="Duration" width="130px" />
<ColumnDirective field="Progress" headerText="Progress" />
</ColumnsDirective>
<Inject services={[Selection, Toolbar, Edit, Filter, Sort, DayMarkers]} />
</GanttComponent>
);
}Key WBS APIs:
| Property | Type | Description |
|---|---|---|
enableWBS | boolean | Auto-generates WBS codes from task hierarchy |
enableAutoWbsUpdate | boolean | Recalculates WBS codes after CRUD, sort, DnD |
WBSCode field | column field | Displays the auto-calculated WBS code |
WBSPredecessor field | column field | Shows WBS-based predecessor codes |
Manual WBS with wbsPredecessor in taskFields
Use taskFields.wbsPredecessor when your data has pre-defined WBS codes:
const taskFields = {
id: 'TaskID',
name: 'TaskName',
wbsPredecessor: 'WBSCode', // map if your data has pre-defined WBS codes
};
// Also add the WBS column:
<ColumnDirective field="WBSCode" headerText="WBS" width="120" />Note: Gantt calculates WBS codes automatically based on tree structure depth when enableWBS is true. No manual data mapping is needed in that case.---
Column Headers and Formatting
Date Formatting
<ColumnDirective field="StartDate" format="yMd" /> {/* locale-aware */}
<ColumnDirective field="StartDate" format="dd/MM/yyyy" /> {/* custom pattern */}
<ColumnDirective field="StartDate" format={{ type: 'date', skeleton: 'full' }} />Number Formatting
<ColumnDirective field="Cost" format="C2" /> {/* currency 2 decimals */}
<ColumnDirective field="Progress" format="P2" /> {/* percentage 2 decimals */}
<ColumnDirective field="Rate" format="N2" /> {/* number 2 decimals */}Header Text Alignment
<ColumnDirective field="TaskName" headerTextAlign="Center" textAlign="Left" />Programmatic alignment change (affects all columns):
const ganttRef = React.useRef<GanttComponent>(null);
function changeAllHeaderAlignment(align: string) {
(ganttRef.current as any).treeGrid.grid.columns.forEach((col: any) => {
col.headerTextAlign = align; // 'Left' | 'Center' | 'Right' | 'Justify'
});
(ganttRef.current as any).treeGrid.grid.refreshHeader();
}Header Template
Render custom React elements inside column headers using headerTemplate:
const headerTemplate = (props: any) => (
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
<img src={`${props.field}.png`} style={{ height: 20 }} />
<span>{props.field}</span>
</div>
);
<ColumnDirective
field="TaskName"
headerText="Task Name"
headerTemplate={headerTemplate}
/>headerTemplatetakes precedence overheaderText. Use it for icon-augmented or rich headers.
Header Text Wrapping
Enable text wrapping on long column headers so they wrap at word boundaries:
// Control via treeGrid instance after load
function load() {
(ganttRef.current as any).treeGrid.allowTextWrap = true;
(ganttRef.current as any).treeGrid.textWrapSettings = { wrapMode: 'Header' };
// wrapMode: 'Header' | 'Content' | 'Both'
}
<GanttComponent ref={ganttRef} load={load} .../>Dynamic Header Text Update
Update column header text at runtime using getColumnByField + refreshHeader:
const ganttRef = React.useRef<GanttComponent>(null);
function renameHeader(field: string, newText: string) {
const col = (ganttRef.current as any).treeGrid.grid.getColumnByField(field);
if (col) {
col.headerText = newText;
(ganttRef.current as any).treeGrid.grid.refreshHeader();
}
}Useful column/header lookup methods:
| Method | Returns |
|---|---|
getColumnByField(field) | Column object by field name |
getColumnHeaderByField(field) | Header DOM element by field name |
getColumnIndexByField(field) | Column index by field name |
getColumnByUid(uid) | Column object by UID |
getColumnHeaderByIndex(index) | Header DOM element by index |
---
Column Templates (Custom Cell Rendering)
Render custom React elements inside cells:
// Progress bar template
const progressTemplate = (props: any) => {
return (
<div style={{ background: '#e0e0e0', borderRadius: 4, height: 10 }}>
<div
style={{
width: `${props.Progress}%`,
background: '#4caf50',
height: 10,
borderRadius: 4,
}}
/>
</div>
);
};
<ColumnDirective
field="Progress"
headerText="Progress"
width="150"
template={progressTemplate}
/>Edit Cell Template
For custom editing input inside a cell:
const editTemplate = (props: any) => (
<input
type="number"
min={0} max={100}
defaultValue={props.Progress}
className="e-input"
/>
);
<ColumnDirective
field="Progress"
template={progressTemplate}
editTemplate={editTemplate}
/>---
Column Spanning
Make a cell span across multiple columns using colSpanAttributes on ColumnDirective:
// Row spanning: set colSpan per-row in rowDataBound or via custom attributesOr use columns.customAttributes with a span value. For header spanning, use headerTemplate:
// Group columns under a common header using stacked headers:
const stackedColumns = [
{
headerText: 'Schedule',
columns: [
{ field: 'StartDate', headerText: 'Start', width: 130 },
{ field: 'EndDate', headerText: 'End', width: 130 },
],
},
];---
Frozen (Pinned) Columns
Pin the first N columns so they remain visible while scrolling horizontally:
import { Freeze } from '@syncfusion/ej2-react-gantt';
<GanttComponent
frozenColumns={2} // first 2 columns are frozen
...
>
<Inject services={[Freeze]} />
</GanttComponent>Inject the Freeze module to enable frozen columns.---
Showing and Hiding Columns
Declarative (initial state):
<ColumnDirective field="Notes" visible={false} /> {/* hidden by default */}Programmatic toggle:
const ganttRef = React.useRef<GanttComponent>(null);
ganttRef.current?.showColumn(['TaskID', 'Notes']); // show by headerText
ganttRef.current?.hideColumn(['Notes']); // hide by headerTextData Binding in Syncfusion React Gantt Chart
Table of Contents
- Overview
- Hierarchical Data Binding
- Self-Referential (Flat) Data Binding
- Remote Data Binding
- URL Adaptor
- Sending Additional Parameters to Server
- Binding with Ajax
- Handling HTTP Errors
- Load Child on Demand
- Split Tasks in Data Source
- Performance: Disable Auto Validation
- Common Pitfalls
---
Overview
The Gantt component accepts data via the dataSource prop. Two formats are supported:
- Local data: JavaScript object array (hierarchical or flat)
- Remote data: A
DataManagerinstance pointing to a REST API
---
Hierarchical Data Binding
Tasks are nested using a child array. Map the array property name to taskFields.child.
import * as React from 'react';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
import { TaskFieldsModel } from '@syncfusion/ej2-gantt';
const hierarchyData = [
{
TaskID: 1,
TaskName: 'Project Initiation',
StartDate: new Date('04/02/2024'),
EndDate: new Date('04/21/2024'),
subtasks: [
{ TaskID: 2, TaskName: 'Identify Site Location', StartDate: new Date('04/02/2024'), Duration: 4, Progress: 50 },
{ TaskID: 3, TaskName: 'Perform Soil Test', StartDate: new Date('04/02/2024'), Duration: 4, Progress: 50 },
{ TaskID: 4, TaskName: 'Soil Test Approval', StartDate: new Date('04/08/2024'), Duration: 0, Progress: 50 },
],
},
];
const taskFields: TaskFieldsModel = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
progress: 'Progress',
child: 'subtasks', // <-- maps the nested children array
};
function App() {
return <GanttComponent dataSource={hierarchyData} taskFields={taskFields} height="400px" />;
}---
Self-Referential (Flat) Data Binding
All tasks are in a flat array. Parent–child relationships are expressed via a parentID field. Use taskFields.parentID to map it.
const flatData = [
{ TaskID: 1, TaskName: 'Project Initiation', StartDate: new Date('04/02/2024'), EndDate: new Date('04/21/2024') },
{ TaskID: 2, TaskName: 'Identify Site Location', StartDate: new Date('04/02/2024'), Duration: 4, Progress: 50, ParentID: 1 },
{ TaskID: 3, TaskName: 'Perform Soil Test', StartDate: new Date('04/02/2024'), Duration: 4, Progress: 50, ParentID: 1 },
{ TaskID: 4, TaskName: 'Soil Test Approval', StartDate: new Date('04/08/2024'), Duration: 4, Progress: 50, ParentID: 1 },
{ TaskID: 5, TaskName: 'Project Estimation', StartDate: new Date('04/02/2024'), EndDate: new Date('04/21/2024') },
{ TaskID: 6, TaskName: 'Develop Floor Plan', StartDate: new Date('04/04/2024'), Duration: 3, Progress: 50, ParentID: 5 },
];
const taskFields: TaskFieldsModel = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
progress: 'Progress',
parentID: 'ParentID', // <-- flat data parent reference
};
function App() {
return <GanttComponent dataSource={flatData} taskFields={taskFields} height="400px" />;
}Note: Do NOT map bothchildandparentIDat the same time. If both are mapped,parentIDtakes priority and hierarchical nesting is ignored.
---
Remote Data Binding
Use DataManager with an adaptor to connect to a REST API.
WebApiAdaptor
import { DataManager, WebApiAdaptor } from '@syncfusion/ej2-data';
const dataSource = new DataManager({
url: '/api/GanttData',
adaptor: new WebApiAdaptor,
crossDomain: true,
});
const taskFields: TaskFieldsModel = {
id: 'TaskId',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
dependency: 'Predecessor',
child: 'SubTasks',
};
function App() {
return <GanttComponent dataSource={dataSource} taskFields={taskFields} height="450px" />;
}RemoteSaveAdaptor (client-side data, server-side CRUD)
Use this when you want local rendering but persist changes via server:
import { DataManager, RemoteSaveAdaptor } from '@syncfusion/ej2-data';
const dataSource = new DataManager({
json: localData, // local data array
batchUrl: '/api/BatchUpdate', // server endpoint for CRUD
adaptor: new RemoteSaveAdaptor,
});The server endpoint receives a payload with added, changed, and deleted arrays and should return { addedRecords, changedRecords, deletedRecords }.
Handling Server Errors
function actionFailure(args: any) {
console.error('Gantt server error:', args);
}
<GanttComponent actionFailure={actionFailure} ... />---
URL Adaptor
Use UrlAdaptor to bind Gantt to an ADO.NET Entity Data Model / MVC controller endpoint. The server returns { result, count } JSON:
import { DataManager, UrlAdaptor } from '@syncfusion/ej2-data';
const dataSource = new DataManager({
url: '/Home/UrlDatasource',
adaptor: new UrlAdaptor,
batchUrl: '/Home/BatchSave', // for CRUD operations
});
const taskFields: TaskFieldsModel = {
id: 'TaskId',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
dependency: 'Predecessor',
child: 'SubTasks',
};
function App() {
return <GanttComponent dataSource={dataSource} taskFields={taskFields} height="450px" />;
}The server endpoint should return { result: [...], count: N } JSON.---
Sending Additional Parameters to Server
Pass custom parameters to the server using Query.addParams() in the load event:
import { DataManager, UrlAdaptor, Query } from '@syncfusion/ej2-data';
import { GanttComponent, Inject, Edit, Toolbar } from '@syncfusion/ej2-react-gantt';
const dataSource = new DataManager({
url: '/Home/UrlDatasource',
adaptor: new UrlAdaptor,
batchUrl: '/Home/BatchSave',
});
function App() {
let ganttInstance: GanttComponent;
function load() {
ganttInstance.query = new Query().addParams('ej2Gantt', 'customValue');
}
return (
<GanttComponent
dataSource={dataSource}
taskFields={taskFields}
load={load}
ref={gantt => (ganttInstance = gantt!)}
editSettings={{ allowAdding: true, allowEditing: true, allowDeleting: true }}
toolbar={['Add', 'Edit', 'Delete', 'Cancel', 'Update', 'ExpandAll', 'CollapseAll']}
height="450px"
>
<Inject services={[Edit, Toolbar]} />
</GanttComponent>
);
}The server receives the extra parameter alongside the standardDataManagerRequestpayload and can read it by name (e.g.,ej2Gantt).
---
Binding with Ajax
Fetch data via Ajax and assign it to dataSource at runtime. The component behaves like a local data source after assignment:
import { Ajax } from '@syncfusion/ej2-base';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
function App() {
let ganttInstance: any;
function clickHandler() {
const ajax = new Ajax('/api/GanttData', 'GET');
ganttInstance.showSpinner();
ajax.send();
ajax.onSuccess = (data: string) => {
ganttInstance.hideSpinner();
ganttInstance.dataSource = (JSON.parse(data)).Items;
ganttInstance.refresh();
};
}
return (
<div>
<button onClick={clickHandler}>Bind Data</button>
<GanttComponent
taskFields={taskFields}
projectStartDate="02/24/2019"
projectEndDate="07/20/2019"
height="450px"
ref={gantt => (ganttInstance = gantt)}
/>
</div>
);
}Note: Data loaded via Ajax acts as local data — server-side CRUD is not supported after assignment this way.
---
Handling HTTP Errors
Use the actionFailure event to catch server-side errors (e.g., 404, 500) and display a user-facing message:
import { DataManager } from '@syncfusion/ej2-data';
const dataSource = new DataManager({ url: '/api/GanttData' });
function App() {
let ganttInstance: any;
function actionFailure(args: any) {
const span = document.createElement('span');
ganttInstance.element.parentNode.insertBefore(span, ganttInstance.element);
span.style.color = '#FF0000';
span.innerHTML = 'Server exception: ' + (args.error?.status ?? 'Unknown error');
}
return (
<GanttComponent
dataSource={dataSource}
taskFields={taskFields}
actionFailure={actionFailure}
height="450px"
ref={gantt => (ganttInstance = gantt)}
/>
);
}---
Load Child on Demand
Renders only root nodes initially and loads children from the server on expand. Useful for very large datasets.
import { DataManager, WebApiAdaptor } from '@syncfusion/ej2-data';
import { VirtualScroll, Selection } from '@syncfusion/ej2-react-gantt';
const dataSource = new DataManager({
url: '/api/GanttLoadOnDemand',
adaptor: new WebApiAdaptor,
crossDomain: true,
});
const taskFields: TaskFieldsModel = {
id: 'taskId',
name: 'taskName',
startDate: 'startDate',
endDate: 'endDate',
duration: 'duration',
progress: 'progress',
parentID: 'parentID',
hasChildMapping: 'isParent', // tells Gantt which records have children
expandState: 'IsExpanded', // persists expand/collapse state
};
function App() {
return (
<GanttComponent
dataSource={dataSource}
taskFields={taskFields}
enableVirtualization={true}
loadChildOnDemand={false} // false = load children on expand
height="460px"
>
<Inject services={[Selection, VirtualScroll]} />
</GanttComponent>
);
}Limitations of load-on-demand: Filtering, sorting, and searching are not supported. Only self-referential data works.
---
Split Tasks in Data Source
Define task segments to split a task into multiple intervals.
Hierarchical (inline)
const data = [
{
TaskID: 1,
TaskName: 'Development',
StartDate: new Date('04/02/2024'),
Duration: 6,
Segments: [
{ StartDate: new Date('04/02/2024'), Duration: 2 },
{ StartDate: new Date('04/05/2024'), Duration: 4 },
],
},
];
const taskFields: TaskFieldsModel = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
segments: 'Segments', // map the segments field
};Self-referential (flat segment data)
const segmentData = [
{ SegmentId: 1, StartDate: new Date('04/02/2024'), Duration: 2 },
{ SegmentId: 1, StartDate: new Date('04/05/2024'), Duration: 2 },
];
const taskFields: TaskFieldsModel = {
segmentId: 'SegmentId',
};
<GanttComponent taskFields={taskFields} segmentData={segmentData} ... />---
Performance: Disable Auto Validation
For very large datasets where all dates/durations are already correct, disable auto-validation to speed up initial load:
<GanttComponent
dataSource={largeData}
taskFields={taskFields}
autoCalculateDateScheduling={false}
height="450px"
/>WhenautoCalculateDateScheduling={false}, parent-child validation, data validation, and predecessor validation are all skipped. Ensure your data source has correctStartDate,EndDate, andDurationvalues for every task before using this.
---
Common Pitfalls
| Issue | Cause | Fix |
|---|---|---|
| Tasks not rendered | Wrong field name in taskFields | Check exact field name matches (case-sensitive) |
| All tasks appear as root | parentID field not mapped or wrong name | Verify taskFields.parentID matches data field |
| Parent dates wrong in auto mode | Parent dates in data conflict with child dates | Remove parent StartDate/EndDate; they are auto-calculated |
| Remote data not loading | CORS or wrong adaptor | Verify API URL and use correct adaptor type |
| Segments not showing | segments field not mapped | Add taskFields.segments: 'Segments' |
Events
Table of Contents
- Event Architecture Overview
- Core Events Reference
- Cancellable Events Summary
- `requestType` Quick Reference
- All Events Quick Reference
- Complete Example — Multiple Events
---
Event Architecture Overview
Gantt events follow a before/after pattern:
- `actionBegin` — fires before an action, allows
args.cancel = trueto abort - `actionComplete` — fires after an action completes
Many events have requestType to identify the specific operation.
---
Core Events Reference
actionBegin
Fires before: add, edit (cell/dialog/taskbar), delete, sort, filter, dependency change, zoom, time span navigation.
`requestType` values:
| Value | Operation |
|---|---|
'beforeAdd' | Before a task is added |
'beforeDelete' | Before a task is deleted |
'beforeSave' | Before cell/dialog edit is saved |
'taskbarEditing' | Before taskbar drag edit |
'filtering' | Before filter applied |
'sorting' | Before sort applied |
'beforeZoomIn' | Before zoom in |
'beforeZoomOut' | Before zoom out |
'validateDependency' | Before dependency link drawn |
const actionBegin = (args: any) => {
if (args.requestType === 'beforeSave') {
// Validate task data before saving
if (args.data && args.data.Duration < 0) {
args.cancel = true; // prevent save
}
}
if (args.requestType === 'beforeDelete') {
console.log('Deleting task:', args.data);
}
if (args.requestType === 'validateDependency') {
if (!args.isValidLink) {
args.cancel = true; // prevent invalid dependency
}
}
};
<GanttComponent actionBegin={actionBegin} ... />Key `actionBegin` args properties:
| Property | Type | Description |
|---|---|---|
requestType | string | Type of action |
cancel | boolean | Set true to abort the action |
data | object | Task data involved |
newTaskData | object | Newly added task data |
recordIndex | number | Index of affected record |
rowPosition | string | 'Top', 'Bottom', 'Above', 'Below' |
fromItem | IGanttData | Predecessor task (dependency events) |
toItem | IGanttData | Successor task (dependency events) |
isValidLink | boolean | Whether dependency link is valid |
columnName | string | Column name (sort/filter events) |
direction | string | 'Ascending' or 'Descending' (sort) |
---
actionComplete
Fires after: add, edit, delete, sort, filter, zoom, dependency changes.
const actionComplete = (args: any) => {
if (args.requestType === 'add') {
console.log('Task added:', args.newTaskData);
// Sync to server
}
if (args.requestType === 'delete') {
console.log('Deleted records:', args.modifiedRecords);
}
if (args.requestType === 'sorting') {
console.log('Sorted by:', args.columnName, args.direction);
}
};
<GanttComponent actionComplete={actionComplete} ... />Key `actionComplete` args properties:
| Property | Type | Description |
|---|---|---|
requestType | string | Type of action completed |
action | string | Action performed |
data | object | Task data involved |
newTaskData | object | Newly added task data |
modifiedRecords | object[] | Records modified during action |
modifiedTaskData | object[] | Task data after modification |
recordIndex | number | Index of modified record |
timeline | object | Timeline settings (zoom events) |
---
taskbarEditing
Fires continuously while a taskbar is being dragged or resized.
const taskbarEditing = (args: any) => {
// Prevent editing tasks marked as locked
if (args.data.taskData.IsLocked) {
args.cancel = true;
}
// Log real-time editing
console.log(`Editing: ${args.data.TaskName}, action: ${args.taskBarEditAction}`);
};`taskbarEditing` args:
| Property | Type | Description |
|---|---|---|
action | string | Type of edit in progress |
cancel | boolean | Set true to cancel the edit |
data | IGanttData | Task being edited |
editingFields | ITaskData | Fields currently being modified |
previousData | ITaskData | Task data before the edit |
recordIndex | number | Index of the task |
taskBarEditAction | string | Specific action (e.g., 'LeftResizing', 'RightResizing', 'Move', 'ProgressResizing') |
---
taskbarEdited
Fires after a taskbar drag or resize is completed.
const taskbarEdited = (args: any) => {
console.log('Taskbar edit complete:', args.data.TaskName);
console.log('New dates:', args.data.ganttProperties.startDate, args.data.ganttProperties.endDate);
// Persist changes to server
};
<GanttComponent taskbarEdited={taskbarEdited} ... />---
queryTaskbarInfo
Fires for each taskbar render, allowing conditional styling.
const queryTaskbarInfo = (args: any) => {
if (args.data.Progress < 30) {
args.taskbarBgColor = '#ff6b6b';
args.progressBarBgColor = '#cc0000';
}
};
<GanttComponent queryTaskbarInfo={queryTaskbarInfo} ... />`queryTaskbarInfo` args:
| Property | Description |
|---|---|
data | Task record data |
taskbarBgColor | Set taskbar background color |
progressBarBgColor | Set progress bar color |
taskbarBorderColor | Set taskbar border color |
rowData | Full row data |
---
rowSelected / rowDeselected
const rowSelected = (args: any) => {
console.log('Selected row index:', args.rowIndex);
console.log('Selected data:', args.data);
};
const rowDeselected = (args: any) => {
console.log('Deselected:', args.data);
};
<GanttComponent rowSelected={rowSelected} rowDeselected={rowDeselected} ... />---
cellSelected / cellDeselected
const cellSelected = (args: any) => {
console.log('Cell selected:', args.cellIndex, args.data);
};
<GanttComponent cellSelected={cellSelected} ... />---
created
Fires after the Gantt is initialized and the initial render is complete.
<GanttComponent created={() => console.log('Gantt initialized')} ... />---
dataBound
Fires after data binding completes (initial load or after refresh).
<GanttComponent dataBound={() => console.log('Data bound, rows:', ganttRef.current?.currentViewData.length)} ... />---
load
Fires before the initial data bind and render.
<GanttComponent load={() => console.log('Gantt loading...')} ... />---
beforeTooltipRender
Fires before a tooltip is displayed. Allows customizing tooltip content.
const beforeTooltipRender = (args: any) => {
// Customize the tooltip content
args.content = `<b>${args.data.TaskName}</b><br/>Progress: ${args.data.Progress}%`;
};
<GanttComponent beforeTooltipRender={beforeTooltipRender} ... />---
taskbarClick
Fires when the user clicks on a taskbar.
const taskbarClick = (args: any) => {
console.log('Clicked task:', args.data.TaskName);
console.log('Task ID:', args.data.TaskID);
};
<GanttComponent taskbarClick={taskbarClick} ... />`taskbarClick` args:
| Property | Type | Description |
|---|---|---|
data | IGanttData | The task record that was clicked |
target | Element | The DOM element that was clicked |
---
collapsed / expanded
Fires after a row is collapsed or expanded (parent task toggled).
const onCollapsed = (args: any) => {
console.log('Collapsed row:', args.data.TaskName);
};
const onExpanded = (args: any) => {
console.log('Expanded row:', args.data.TaskName);
};
<GanttComponent collapsed={onCollapsed} expanded={onExpanded} ... />---
expanding / collapsing
Fires before a row expands or collapses. Set args.cancel = true to prevent the operation.
const onCollapsing = (args: any) => {
if (args.data.TaskID === 1) {
args.cancel = true; // prevent collapsing root task
}
};
<GanttComponent collapsing={onCollapsing} ... />---
splitterResized
Fires after the splitter between TreeGrid and Chart panels is resized.
<GanttComponent
splitterResized={(args) => console.log('Splitter position:', args.newWidth)}
...
/>---
excelExportComplete / pdfExportComplete
<GanttComponent
excelExportComplete={(args) => console.log('Excel export done')}
pdfExportComplete={(args) => console.log('PDF export done')}
...
/>---
Cancellable Events Summary
| Event | Cancel Pattern | Effect |
|---|---|---|
actionBegin | args.cancel = true | Prevents the action (add/edit/delete/sort/filter/dependency) |
taskbarEditing | args.cancel = true | Prevents the taskbar drag/resize change |
collapsing | args.cancel = true | Prevents the row from collapsing |
expanding | args.cancel = true | Prevents the row from expanding |
---
requestType Quick Reference
requestType | Event | Meaning |
|---|---|---|
beforeAdd | actionBegin | Before task addition |
add | actionComplete | After task addition |
beforeSave | actionBegin | Before cell/dialog save |
save | actionComplete | After edit saved |
beforeDelete | actionBegin | Before task deletion |
delete | actionComplete | After deletion |
taskbarEditing | actionBegin | Taskbar drag starting |
filtering | actionBegin/Complete | Filter operation |
filterAfterOpen | actionComplete | Filter menu opened |
sorting | actionBegin/Complete | Sort operation |
beforeZoomIn | actionBegin | Before zoom in |
beforeZoomOut | actionBegin | Before zoom out |
validateDependency | actionBegin | Dependency link being drawn |
updateDependency | actionBegin | Dependency being updated |
collapsingRecord | actionBegin | Before a row collapses |
expandingRecord | actionBegin | Before a row expands |
---
All Events Quick Reference
| Event | Cancellable | Description |
|---|---|---|
load | No | Before initial bind and render |
created | No | After Gantt fully initialized |
dataBound | No | After data binding completes |
actionBegin | Yes (args.cancel) | Before any data action or navigation |
actionComplete | No | After any data action completes |
taskbarEditing | Yes (args.cancel) | During taskbar drag/resize |
taskbarEdited | No | After taskbar drag/resize completes |
taskbarClick | No | When user clicks a taskbar |
queryTaskbarInfo | No | Per-taskbar render (apply dynamic styles) |
rowSelected | No | After a row is selected |
rowDeselected | No | After a row is deselected |
cellSelected | No | After a cell is selected |
cellDeselected | No | After a cell is deselected |
collapsing | Yes (args.cancel) | Before a row collapses |
collapsed | No | After a row collapses |
expanding | Yes (args.cancel) | Before a row expands |
expanded | No | After a row expands |
beforeTooltipRender | No | Before tooltip is displayed (allows content override) |
splitterResizeStart | No | Before splitter drag begins |
splitterResizing | No | During splitter drag |
splitterResized | No | After splitter drag ends |
excelExportComplete | No | After Excel export finishes |
pdfExportComplete | No | After PDF export finishes |
contextMenuClick | No | When a context menu item is clicked |
contextMenuOpen | No | Before context menu opens (use args.hideItems) |
toolbarClick | No | When a toolbar item is clicked |
resizeStart | No | Before column resize starts |
resizing | No | During column resize |
resizeStop | No | After column resize ends |
columnDragStart | No | Before column reorder drag starts |
columnDrag | No | During column reorder drag |
columnDrop | No | After column dropped in new position |
---
Complete Example — Multiple Events
import React, { useRef } from 'react';
import {
GanttComponent, Inject, Edit, Selection, Toolbar, Sort, Filter,
IQueryTaskbarInfoEventArgs, EditSettingsModel, TaskFieldsModel
} from '@syncfusion/ej2-react-gantt';
function App() {
const ganttRef = useRef<GanttComponent>(null);
const taskFields: TaskFieldsModel = {
id: 'TaskID', name: 'TaskName',
startDate: 'StartDate', duration: 'Duration',
progress: 'Progress', dependency: 'Predecessor', child: 'subtasks'
};
const editSettings: EditSettingsModel = {
allowAdding: true, allowEditing: true,
allowDeleting: true, allowTaskbarEditing: true
};
// Validation before save
const actionBegin = (args: any) => {
if (args.requestType === 'beforeSave') {
if (args.data && args.data.Duration < 0) {
args.cancel = true;
alert('Duration must be positive.');
}
}
};
// Post-action logging
const actionComplete = (args: any) => {
if (args.requestType === 'add') {
console.log('Added:', args.newTaskData);
}
};
// Dynamic taskbar styling
const queryTaskbarInfo = (args: IQueryTaskbarInfoEventArgs) => {
const progress = (args.data as any).Progress;
args.taskbarBgColor = progress < 30 ? '#ff6b6b' : progress < 70 ? '#ffa500' : '#4CAF50';
};
// Taskbar drag validation
const taskbarEditing = (args: any) => {
if ((args.data as any).taskData?.IsLocked) {
args.cancel = true;
}
};
return (
<GanttComponent
ref={ganttRef}
dataSource={data}
taskFields={taskFields}
editSettings={editSettings}
toolbar={['Add', 'Edit', 'Delete', 'Update', 'Cancel']}
allowSorting={true}
allowFiltering={true}
actionBegin={actionBegin}
actionComplete={actionComplete}
queryTaskbarInfo={queryTaskbarInfo}
taskbarEditing={taskbarEditing}
dataBound={() => console.log('Data loaded')}
height='450px'
>
<Inject services={[Edit, Selection, Toolbar, Sort, Filter]} />
</GanttComponent>
);
}
export default App;Excel Export
Table of Contents
- Setup
- Export with Properties
- Custom Data Source Export
- Multiple Gantt Export
- CSV Export
- Export Events
- Common Pitfalls
---
Setup
Enable Excel and CSV export with allowExcelExport={true} and inject ExcelExport.
import { GanttComponent, Inject, Toolbar, ExcelExport, Selection } from '@syncfusion/ej2-react-gantt';
import { ClickEventArgs } from '@syncfusion/ej2-navigations';
function App() {
const ganttRef = useRef<any>(null);
const toolbarOptions = ['ExcelExport', 'CsvExport'];
const toolbarClick = (args: ClickEventArgs) => {
if (args.item.id === 'GanttExport_excelexport') {
ganttRef.current.excelExport();
} else if (args.item.id === 'GanttExport_csvexport') {
ganttRef.current.csvExport();
}
};
return (
<GanttComponent
id='GanttExport'
ref={ganttRef}
dataSource={data}
taskFields={taskFields}
toolbar={toolbarOptions}
toolbarClick={toolbarClick}
allowExcelExport={true}
height='400px'
>
<Inject services={[Toolbar, ExcelExport, Selection]} />
</GanttComponent>
);
}Toolbar item IDs follow the pattern{ganttId}_{action}e.g.GanttExport_excelexport,GanttExport_csvexport.
---
Export with Properties
Pass ExcelExportProperties to customize the export:
import { ExcelExportProperties } from '@syncfusion/ej2-react-gantt';
const toolbarClick = (args: ClickEventArgs) => {
if (args.item.id === 'GanttExport_excelexport') {
const exportProps: ExcelExportProperties = {
fileName: 'project-schedule.xlsx',
dataSource: customData, // optional: override data
includeHiddenColumn: true // include hidden columns
};
ganttRef.current.excelExport(exportProps);
}
};ExcelExportProperties Key Options
| Property | Type | Description |
|---|---|---|
fileName | string | Output file name |
dataSource | object[] | Override data source for export |
includeHiddenColumn | boolean | Export hidden columns |
theme | object | Custom Excel theme/styling |
---
Custom Data Source Export
Export a specific subset of data dynamically:
const toolbarClick = (args: ClickEventArgs) => {
if (args.item.id === 'GanttExport_excelexport') {
const excelExportProperties: ExcelExportProperties = {
dataSource: [data[0], data[1]] // export only first two records
};
ganttRef.current.excelExport(excelExportProperties);
}
};---
Multiple Gantt Export
Export data from multiple Gantt instances to the same Excel sheet or separate sheets.
Same Sheet (AppendToSheet)
const toolbarClick = (args: ClickEventArgs) => {
if (args.item.id === 'FirstGantt_excelexport') {
const appendProps: ExcelExportProperties = {
multipleExport: { type: 'AppendToSheet', blankRows: 2 }
};
// Export first Gantt, then chain second export with shared workbook data
const firstExport: Promise<any> = firstGanttRef.current.excelExport(appendProps, true);
firstExport.then((workbookData: any) => {
secondGanttRef.current.excelExport(appendProps, false, workbookData);
});
}
};New Sheet (NewSheet)
const appendProps: ExcelExportProperties = {
multipleExport: { type: 'NewSheet' }
};
const firstExport: Promise<any> = firstGanttRef.current.excelExport(appendProps, true);
firstExport.then((workbookData: any) => {
secondGanttRef.current.excelExport(appendProps, false, workbookData);
});`multipleExport` options:
| Property | Values | Description |
|---|---|---|
type | 'AppendToSheet' \ | 'NewSheet' |
blankRows | number | Blank rows between Gantt data (AppendToSheet only) |
Multiple Export Setup
// Each Gantt instance needs allowExcelExport={true} and ExcelExport injected
<GanttComponent id='FirstGantt' ref={firstGanttRef} allowExcelExport={true} toolbar={['ExcelExport']} toolbarClick={toolbarClick} ...>
<Inject services={[Toolbar, ExcelExport, Selection]} />
</GanttComponent>
<GanttComponent id='SecondGantt' ref={secondGanttRef} allowExcelExport={true} ...>
<Inject services={[ExcelExport, Selection]} />
</GanttComponent>---
CSV Export
CSV export uses the same allowExcelExport flag and ExcelExport module:
const toolbarClick = (args: ClickEventArgs) => {
if (args.item.id === 'GanttExport_csvexport') {
ganttRef.current.csvExport();
}
};---
Export Events
<GanttComponent
excelExportComplete={(args) => console.log('Export done', args)}
excelQueryCellInfo={(args) => {
// Customize individual cell styles/values before export
if (args.column.field === 'Progress' && args.value > 80) {
args.style = { backgroundColor: '#c8e6c9' };
}
}}
...
/>---
Common Pitfalls
| Issue | Cause | Fix |
|---|---|---|
| Export not triggering | ExcelExport service not injected | Add ExcelExport to services |
| Export not triggering | allowExcelExport not set | Set allowExcelExport={true} |
| Wrong toolbar ID in click handler | Gantt id not matching | Toolbar item ID = {ganttId}_excelexport |
| Multiple export chaining fails | Not using Promise chain | Use .then() on first export's return value |
| CSV same as Excel | Same setup — csvExport() handles CSV format | Use ganttRef.current.csvExport() separately |
Filtering and Searching in Syncfusion React Gantt Chart
Table of Contents
- Enable Filtering
- Filter Hierarchy Modes
- Filter Menu
- Excel-Like Filter
- Toolbar Search
- Programmatic Filtering
- Initial Filter State (Pre-filtered on Load)
- Custom Filter Predicates
---
Enable Filtering
import { GanttComponent, Inject, Filter } from '@syncfusion/ej2-react-gantt';
<GanttComponent allowFiltering={true} ...>
<Inject services={[Filter]} />
</GanttComponent>When enabled, each column header shows a filter icon. Click it to open the filter menu.
---
Filter Hierarchy Modes
Control how parent/child rows are shown with filtered results:
| Mode | Behavior |
|---|---|
"Parent" (default) | Shows filtered records + their parent records |
"Child" | Shows filtered records + their child records |
"Both" | Shows filtered records + both parents and children |
"None" | Shows only the filtered records (no hierarchy) |
import { FilterSettingsModel } from '@syncfusion/ej2-gantt';
const filterSettings: FilterSettingsModel = {
hierarchyMode: 'Both',
};
<GanttComponent allowFiltering={true} filterSettings={filterSettings} ...>
<Inject services={[Filter]} />
</GanttComponent>---
Filter Menu
The filter menu appears when clicking a column's filter icon. It supports operators based on column type:
- String columns: Contains, Does Not Contain, Starts With, Ends With, Equal, Not Equal
- Number columns: Equal, Not Equal, Greater Than, Greater Than or Equal, Less Than, Less Than or Equal
- Date columns: Equal, Not Equal, Greater Than, Less Than, etc.
// Filter type is automatically set based on column type
// Customize per column:
<ColumnDirective
field="Duration"
type="number"
filter={{ type: 'Menu' }} // explicit 'Menu' or 'CheckBox' or 'Excel'
/>---
Excel-Like Filter
The Excel-like filter shows a checklist of unique values per column for quick filtering:
const filterSettings: FilterSettingsModel = {
type: 'Excel', // enables Excel-like filter across all columns
};
<GanttComponent allowFiltering={true} filterSettings={filterSettings} ...>
<Inject services={[Filter]} />
</GanttComponent>Or enable per column:
<ColumnDirective field="TaskName" filter={{ type: 'Excel' }} />---
Toolbar Search
Add a search box to the toolbar to filter rows by text across all columns:
<GanttComponent
toolbar={['Search']}
searchSettings={{ fields: ['TaskName', 'Duration'], operator: 'contains', ignoreCase: true }}
...
>
<Inject services={[Toolbar]} />
</GanttComponent>Search Settings
| Prop | Default | Description |
|---|---|---|
fields | all columns | Which fields to search in |
operator | 'contains' | Search operator |
key | '' | Initial search value |
ignoreCase | true | Case-insensitive search |
Programmatic Search
ganttRef.current?.search('Design'); // search for "Design"
ganttRef.current?.clearSearching(); // clear search---
Programmatic Filtering
import { Predicate } from '@syncfusion/ej2-data';
// Filter a single column
ganttRef.current?.filterByColumn('Duration', 'greaterthan', 3);
// Clear all filters
ganttRef.current?.clearFiltering();
// Clear filter for a specific column
ganttRef.current?.clearFiltering(['Duration']);---
Initial Filter State (Pre-filtered on Load)
Use filterSettings.columns to apply filters when the Gantt first renders — no user interaction needed:
import { FilterSettingsModel } from '@syncfusion/ej2-gantt';
const filterSettings: FilterSettingsModel = {
columns: [
{ field: 'Duration', matchCase: false, operator: 'greaterthan', predicate: 'and', value: 2 },
{ field: 'Progress', matchCase: false, operator: 'greaterthan', predicate: 'and', value: 50 },
],
};
<GanttComponent
allowFiltering={true}
filterSettings={filterSettings}
...
>
<Inject services={[Filter]} />
</GanttComponent>Column filter object properties:
| Property | Type | Description |
|---|---|---|
field | string | Column field name to filter |
operator | string | Filter operator ('contains', 'equal', 'greaterthan', 'lessthan', etc.) |
value | any | Filter value |
predicate | 'and' \ | 'or' |
matchCase | boolean | Case-sensitive matching |
---
Custom Filter Predicates
import { Predicate } from '@syncfusion/ej2-data';
// Apply complex filter: Duration > 3 AND Progress < 50
const predicate1 = new Predicate('Duration', 'greaterthan', 3);
const predicate2 = new Predicate('Progress', 'lessthan', 50);
ganttRef.current?.filterByColumn('Duration', 'greaterthan', 3);
// Chain predicates for multi-column filter via filterSettings.columns:
const filterSettings = {
columns: [
{ field: 'Duration', matchCase: false, operator: 'greaterthan', predicate: 'and', value: 3 },
{ field: 'Progress', matchCase: false, operator: 'lessthan', predicate: 'and', value: 50 },
],
};
<GanttComponent allowFiltering={true} filterSettings={filterSettings} .../>Gantt Chart — Programmatic Methods Reference
Methods not covered in any other reference file. Use as a supplement for programmatic control.
Table of Contents
- autoFitColumns
- cancelEdit
- changeTaskMode
- clearRedoCollection
- clearUndoCollection
- collapseByIndex
- convertToMilestone
- deleteRecord
- enableItems
- expandByIndex
- getCurrentViewData
- getDurationString
- getExpandedRecords
- getGanttColumns
- getGridColumns
- getRecordByID
- getRedoActions
- getRowByID
- getRowByIndex
- getTaskByUniqueID
- getTaskInfo
- getTaskbarHeight
- getUndoActions
- getWorkString
- openAddDialog
- openEditDialog
- removeSortColumn
- reorderRows
- scrollToTask
- selectCells
- updateChartScrollOffset
- updateDataSource
- updateProjectDates
- updateRecordByID
- updateRecordByIndex
- updateTaskId
---
autoFitColumns
Adjusts column width(s) to fit content. Call inside dataBound for initial render.
ganttRef.current?.autoFitColumns('TaskName');
ganttRef.current?.autoFitColumns(['TaskName', 'StartDate', 'Duration']);
ganttRef.current?.autoFitColumns(); // all columns| Parameter | Type | Description |
|---|---|---|
fieldNames (optional) | `string \ | string[]` |
---
cancelEdit
Cancels the active edit operation and reverts unsaved changes. ganttRef.current?.cancelEdit();
---
changeTaskMode
Changes the scheduling mode of a task at runtime (Auto, Manual, or Custom).
const record = ganttRef.current?.getRecordByID('3');
if (record) {
record.taskData.taskMode = 1; // 0=Auto, 1=Manual, 2=Custom
ganttRef.current?.changeTaskMode(record.taskData);
}| Parameter | Type | Description |
|---|---|---|
data | Object | Task data object with updated taskMode. |
---
clearRedoCollection
Clears the redo history stack. ganttRef.current?.clearRedoCollection();
RequiresenableUndoRedo={true}andUndoRedoservice injected.
---
clearUndoCollection
Clears the undo history stack. ganttRef.current?.clearUndoCollection();
RequiresenableUndoRedo={true}andUndoRedoservice injected.
---
collapseByIndex
Collapses a parent row at the given zero-based index. ganttRef.current?.collapseByIndex(2);
Parameter: index — number
---
convertToMilestone
Converts a task into a milestone (sets duration to zero). ganttRef.current?.convertToMilestone('5');
Parameter: id — string
---
deleteRecord
Deletes one or more task records by ID, index, or IGanttData reference.
ganttRef.current?.deleteRecord(3); // single ID
ganttRef.current?.deleteRecord([2, 3, 5]); // multiple IDs
ganttRef.current?.deleteRecord(record); // IGanttData refParameter: taskDetail — number | string | number[] | string[] | IGanttData | IGanttData[]
---
enableItems
Enables or disables toolbar items by their item ID strings.
ganttRef.current?.enableItems(['GanttToolbar_add', 'GanttToolbar_delete'], false);Parameters: items: string[] — toolbar item IDs. isEnable: boolean — true to enable, false to disable.
---
expandByIndex
Expands one or more parent rows by zero-based index. ganttRef.current?.expandByIndex(1); or ganttRef.current?.expandByIndex([0, 2, 4]);
Parameter: index — number | number[]
---
getCurrentViewData
Returns visible task records after all active filters, sorts, and CRUD operations. Returns: Object[]
const visibleRecords: Object[] = ganttRef.current?.getCurrentViewData() ?? [];---
getDurationString
Formats a duration value with its unit into a readable string (e.g. "3 days").
ganttRef.current?.getDurationString(3, 'day'); // "3 days"
ganttRef.current?.getDurationString(8, 'hour'); // "8 hours"| Parameter | Type | Description |
|---|---|---|
duration | number | Numeric duration value. |
durationUnit | string | 'day', 'hour', or 'minute'. |
Returns: string
---
getExpandedRecords
Returns only expanded records from a given collection. Returns: IGanttData[]
const expanded = ganttRef.current?.getExpandedRecords(ganttRef.current.flatData);Parameter: records — IGanttData[]
---
getGanttColumns
Returns the current Gantt column model definitions. Returns: ColumnModel[]
const columns = ganttRef.current?.getGanttColumns();---
getGridColumns
Returns raw TreeGrid Column objects — useful for runtime updates to visible, width, or format. Returns: Column[]
const gridCols = ganttRef.current?.getGridColumns();---
getRecordByID
Retrieves the IGanttData object for a task by its ID string. Returns: IGanttData
const record = ganttRef.current?.getRecordByID('3');Parameter: id — string
---
getRedoActions
Returns the current redo stack. Each item has action (e.g. 'add', 'delete', 'sorting') and modifiedRecords.
const redoStack = ganttRef.current?.getRedoActions();
redoStack?.forEach(item => console.log(item.action));Returns: Object[]
RequiresenableUndoRedo={true}andUndoRedoservice.
---
getRowByID
Returns the DOM HTMLElement for the chart row matching the given task ID. Returns: HTMLElement
const rowEl = ganttRef.current?.getRowByID('5');Parameter: id — string | number
---
getRowByIndex
Returns the DOM HTMLElement for the chart row at the given zero-based index. Returns: HTMLElement
const rowEl = ganttRef.current?.getRowByIndex(0);Parameter: index — number
---
getTaskByUniqueID
Retrieves an IGanttData record using its internal unique ID (not the user-defined task ID). Returns: IGanttData
const record = ganttRef.current?.getTaskByUniqueID('some-unique-id');Parameter: id — string
---
getTaskInfo
Retrieves internal rendering properties (taskbar width, left offset, segments) for a task by ID. Returns: IGanttTaskInfo
const info = ganttRef.current?.getTaskInfo('3');
console.log(info?.width, info?.left, info?.segments);Parameter: taskId — string
---
getTaskbarHeight
Returns the configured taskbar row height in pixels. Returns: number
const height: number = ganttRef.current?.getTaskbarHeight() ?? 0;---
getUndoActions
Returns the current undo stack. Each item has action and modifiedRecords.
const undoStack = ganttRef.current?.getUndoActions();
undoStack?.forEach(item => console.log(item.action));Returns: Object[]
RequiresenableUndoRedo={true}andUndoRedoservice.
---
getWorkString
Formats a work value with its unit into a readable string (e.g. "24 hours").
ganttRef.current?.getWorkString(24, 'hour'); // "24 hours"| Parameter | Type | Description |
|---|---|---|
work | number | Numeric work value. |
workUnit | string | 'day', 'hour', or 'minute'. |
Returns: string
---
openAddDialog
Opens the add task dialog programmatically. ganttRef.current?.openAddDialog();
RequireseditSettings.allowAdding={true}andEditservice injected.
---
openEditDialog
Opens the edit dialog for a specific task ID, or for the currently selected row if omitted.
ganttRef.current?.openEditDialog(3);
ganttRef.current?.openEditDialog(); // selected row| Parameter | Type | Description |
|---|---|---|
taskId (optional) | `number \ | string` |
RequireseditSettings.allowEditing={true}andEditservice injected.
---
removeSortColumn
Removes the sort on a specific column without affecting other sorted columns.
ganttRef.current?.removeSortColumn('StartDate');Parameter: columnName — string
---
reorderRows
Moves rows from source indexes to a target drop position.
ganttRef.current?.reorderRows([2, 3], 0, 'above');
ganttRef.current?.reorderRows([4], 1, 'child');| Parameter | Type | Description |
|---|---|---|
fromIndexes | number[] | Zero-based indexes of rows to move. |
toIndex | number | Zero-based index of the target row. |
position | string | 'above', 'below', or 'child'. |
RequiresallowRowDragAndDrop={true}andRowDDservice injected.
---
scrollToTask
Scrolls the chart timeline to bring the taskbar of the specified task into view.
ganttRef.current?.scrollToTask('5');Parameter: taskId — string
---
selectCells
Selects multiple cells by row and column index pairs.
ganttRef.current?.selectCells([
{ rowIndex: 0, cellIndexes: [1, 2] },
{ rowIndex: 2, cellIndexes: [0] },
]);Parameter: rowCellIndexes — ISelectedCell[] (from @syncfusion/ej2-grids)
RequiresselectionSettings.mode='Cell'andSelectionservice.
---
updateChartScrollOffset
Sets both horizontal and vertical scroll positions of the chart pane simultaneously.
ganttRef.current?.updateChartScrollOffset(500, 200); // left=500px, top=200pxParameters: left: number — horizontal px offset. top: number — vertical px offset.
---
updateDataSource
Replaces the entire data source at runtime with optional project date bounds.
ganttRef.current?.updateDataSource(newData, {
projectStartDate: new Date('2024-01-01'),
projectEndDate: new Date('2024-12-31'),
});| Parameter | Type | Description |
|---|---|---|
dataSource | Object[] | New task data collection. |
args | object | Optional projectStartDate and projectEndDate Date values. |
---
updateProjectDates
Updates the project start/end dates, optionally rounding to timeline unit boundaries.
ganttRef.current?.updateProjectDates(
new Date('2024-01-01'),
new Date('2024-12-31'),
true // round off to nearest timeline unit
);| Parameter | Type | Description |
|---|---|---|
startDate | Date | New project start date. |
endDate | Date | New project end date. |
isTimelineRoundOff | boolean | Round to nearest timeline unit boundary. |
isFrom (optional) | string | Origin context string. |
---
updateRecordByID
Updates an existing task record by primary key ID. Only provided fields are updated.
ganttRef.current?.updateRecordByID({ TaskID: 3, TaskName: 'Updated', Duration: 7 });Parameter: data — Object (task ID + fields to update)
---
updateRecordByIndex
Updates a task record at the specified zero-based row index.
ganttRef.current?.updateRecordByIndex(2, { TaskName: 'Revised', Duration: 4 });Parameters: index: number, data: Object
---
updateTaskId
Changes an existing task ID to a new unique ID. ganttRef.current?.updateTaskId(3, 99);
Parameters: currentId: number | string, newId: number | string
Getting Started with Syncfusion React Gantt Chart
Table of Contents
- Dependencies
- Project Setup
- Install the Package
- Add CSS Imports
- Basic Gantt Implementation
- taskFields Mapping
- Module Injection
- Common Setup Gotchas
- Grid Lines
---
Dependencies
Install the Gantt package and its peer dependencies:
npm install @syncfusion/ej2-react-gantt --saveThe component relies on packages such as @syncfusion/ej2-gantt, @syncfusion/ej2-grids, @syncfusion/ej2-layouts, and @syncfusion/ej2-treegrid.
---
Project Setup
Use Vite for a quick React setup:
# JavaScript
npm create vite@latest my-gantt-app -- --template react
cd my-gantt-app
npm install
# TypeScript
npm create vite@latest my-gantt-app -- --template react-ts
cd my-gantt-app
npm installRun the app with:
npm run dev---
Install the Package
npm install @syncfusion/ej2-react-gantt --save---
Add CSS Imports
Add the required theme styles to src/App.css:
@import "../node_modules/@syncfusion/ej2-base/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-gantt/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-grids/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-layouts/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-lists/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-notifications/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-richtexteditor/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-treegrid/styles/tailwind3.css";Then import the stylesheet in src/App.tsx:
import './App.css';Replace tailwind3 with your preferred theme if needed.---
Basic Gantt Implementation
Minimal working example using hierarchical data:
import * as React from 'react';
import { GanttComponent, ColumnsDirective, ColumnDirective } from '@syncfusion/ej2-react-gantt';
import { TaskFieldsModel } from '@syncfusion/ej2-gantt';
import './App.css';
const data = [
{
TaskID: 1,
TaskName: 'Project Initiation',
StartDate: new Date('04/02/2024'),
EndDate: new Date('04/21/2024'),
subtasks: [
{ TaskID: 2, TaskName: 'Identify Site Location', StartDate: new Date('04/02/2024'), Duration: 4, Progress: 50 },
{ TaskID: 3, TaskName: 'Perform Soil Test', StartDate: new Date('04/02/2024'), Duration: 4, Progress: 50 },
{ TaskID: 4, TaskName: 'Soil Test Approval', StartDate: new Date('04/08/2024'), Duration: 0, Progress: 50 },
],
},
{
TaskID: 5,
TaskName: 'Project Estimation',
StartDate: new Date('04/02/2024'),
EndDate: new Date('04/21/2024'),
subtasks: [
{ TaskID: 6, TaskName: 'Develop Floor Plan', StartDate: new Date('04/04/2024'), Duration: 3, Progress: 50 },
{ TaskID: 7, TaskName: 'List Materials', StartDate: new Date('04/04/2024'), Duration: 3, Progress: 50 },
],
},
];
const taskFields: TaskFieldsModel = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
child: 'subtasks',
};
function App() {
return (
<GanttComponent dataSource={data} taskFields={taskFields} height="450px">
<ColumnsDirective>
<ColumnDirective field="TaskID" width="80" />
<ColumnDirective field="TaskName" headerText="Task Name" width="250" />
<ColumnDirective field="StartDate" />
<ColumnDirective field="Duration" />
<ColumnDirective field="Progress" />
</ColumnsDirective>
</GanttComponent>
);
}
export default App;---
taskFields Mapping
taskFields maps your data source fields to the Gantt model. The required fields are id and name.
const taskFields: TaskFieldsModel = {
id: 'TaskID', // Unique task identifier (required)
name: 'TaskName', // Task display name (required)
startDate: 'StartDate', // Task start date
endDate: 'EndDate', // Task end date
duration: 'Duration', // Duration in days (or with unit: "3 days", "5 hours")
progress: 'Progress', // Completion percentage (0-100)
dependency: 'Predecessor', // Predecessor task IDs for dependencies
child: 'subtasks', // Nested child array (hierarchical data)
parentID: 'ParentID', // Parent task ID (self-referential/flat data)
resourceInfo: 'Resources', // Assigned resources
notes: 'Notes', // Task notes
cssClass: 'CssClass', // Custom CSS class per task
indicators: 'Indicators', // Task indicator icons/labels
segments: 'Segments', // Split task segments
constraintType: 'ConstraintType', // Task constraint type
constraintDate: 'ConstraintDate', // Constraint date
baselineStartDate: 'BaselineStartDate', // Baseline start
baselineEndDate: 'BaselineEndDate', // Baseline end
manual: 'isManual', // Manual scheduling flag
};Use either child for hierarchical data or parentID for flat data, not both.
---
Module Injection
Inject only the services you need:
import {
GanttComponent,
Inject,
Edit, // Cell/dialog/taskbar editing
Filter, // Column filtering
Sort, // Column sorting
Toolbar, // Toolbar items
Selection, // Row/cell selection
ColumnMenu, // Column header menu
Reorder, // Column drag-reorder
Resize, // Column resizing
DayMarkers, // Event markers and holidays
ExcelExport, // Excel export
PdfExport, // PDF export
ContextMenu, // Right-click context menu
VirtualScroll, // Virtual scrolling
UndoRedo, // Undo/redo support
RowDD, // Row drag-and-drop
CriticalPath, // Critical path highlighting
} from '@syncfusion/ej2-react-gantt';
function App() {
return (
<GanttComponent ...>
<Inject services={[Edit, Filter, Sort, Toolbar, Selection]} />
</GanttComponent>
);
}---
Common Setup Gotchas
- Ensure
App.cssis imported inApp.tsx. - Missing any required CSS package can break the UI.
- Start task IDs from
1; avoid0andundefined. - Match
taskFields.idandtaskFields.nameexactly to your data. - Set a height such as
height="450px"; the Gantt needs a fixed height to render. - In Auto mode, parent task dates are calculated from child tasks.
---
Grid Lines
Control the TreeGrid grid lines with gridLines:
<GanttComponent
gridLines='Both'
dataSource={data}
taskFields={taskFields}
height='450px'
/>| Value | Effect |
|---|---|
'Both' | Horizontal and vertical lines |
'Horizontal' | Horizontal lines only |
'Vertical' | Vertical lines only |
'None' | No grid lines |
Default is 'Horizontal'.
---
Output
After rendering, the Gantt chart displays:
- A task hierarchy with parent and child records
- A timeline with taskbars sized by start date and duration
- Progress indicators on each task
- Auto-calculated parent dates in Auto scheduling mode
This provides a simple baseline sample for learning how to bind and render Gantt data in React.
Parent task dates:
- In Auto mode, parent task dates are calculated from child tasks and cannot be manually set.
- To set parent dates manually, use
taskMode="Manual"ortaskMode="Custom".
Holidays, Working Time & Timezone
Table of Contents
- Holidays
- Work Week Configuration
- Include Weekends in Scheduling
- Working Time (Daily Hours)
- Work (Effort-Based Scheduling)
- Timezone
- Common Pitfalls
---
Holidays
Define non-working days that affect task scheduling, durations, and dependency calculations. Requires the DayMarkers service injection.
import { GanttComponent, Inject, DayMarkers } from '@syncfusion/ej2-react-gantt';
const holidays = [
// Single-day holiday
{ from: new Date('2024-12-25'), label: 'Christmas Day', cssClass: 'national-holiday' },
// Multi-day holiday range
{ from: new Date('2024-12-26'), to: new Date('2024-12-27'), label: 'Christmas Break', cssClass: 'company-holiday' },
// Minimal — label only
{ from: new Date('2025-01-01'), label: 'New Year' },
];
function App() {
return (
<GanttComponent
dataSource={data}
taskFields={taskFields}
holidays={holidays}
height='450px'
>
<Inject services={[DayMarkers]} />
</GanttComponent>
);
}Holiday Object Properties
| Property | Type | Required | Description |
|---|---|---|---|
from | Date | ✅ | Start date of the holiday |
to | Date | ❌ | End date (omit for single-day) |
label | string | ❌ | Text shown in the timeline cell |
cssClass | string | ❌ | CSS class applied to the holiday cell |
Effects on Scheduling
- Task durations exclude holidays — end dates are automatically extended
- Successor tasks shift forward to preserve dependency relationships
- Critical path slack calculations account for holidays
- Resources are treated as unavailable during holiday periods
Custom Holiday Styling
/* Style the label inside the holiday span */
.national-holiday .e-span-label {
color: #C0392B;
font-weight: bold;
}
.company-holiday .e-span-label {
color: #2980B9;
}---
Work Week Configuration
Define which days of the week are treated as working days. The default is Monday through Friday.
<GanttComponent
workWeek={['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']}
dataSource={data}
taskFields={taskFields}
height='450px'
/>Valid day values: 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
Example — 6-Day Work Week (Mon–Sat)
<GanttComponent
workWeek={['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']}
...
/>---
Include Weekends in Scheduling
Set includeWeekend={true} to treat Saturday and Sunday as working days for task scheduling and duration calculation:
<GanttComponent
includeWeekend={true}
dataSource={data}
taskFields={taskFields}
height='450px'
/>When includeWeekend={true}, weekend days count toward task duration and dependencies shift accordingly.---
Working Time (Daily Hours)
Define the daily working hours used for hour-level scheduling and effort calculations:
<GanttComponent
dayWorkingTime={[
{ from: 8, to: 12 }, // 8:00 AM – 12:00 PM
{ from: 13, to: 17 }, // 1:00 PM – 5:00 PM (excludes lunch break)
]}
dataSource={data}
taskFields={taskFields}
height='450px'
/>fromandtoare hours in 24-hour format. Default is[{ from: 8, to: 17 }](8 AM to 5 PM).
---
Work (Effort-Based Scheduling)
Map a work field on tasks to enable effort-based scheduling. Work represents the total effort required (in workUnit), and duration is calculated based on resource availability.
const taskFields = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
progress: 'Progress',
resourceInfo: 'resources',
work: 'Work', // total effort field
parentID: 'ParentID',
};
// workUnit: 'Hour' (default) | 'Day' | 'Minute'
<GanttComponent
taskFields={taskFields}
workUnit='Hour'
dataSource={data}
height='450px'
>
<Inject services={[Edit]} />
</GanttComponent>Mapping taskFields.work sets the default task type to FixedWork — work hours stay constant when resources or duration change.Task Types
| Type | Behavior |
|---|---|
FixedWork | Work stays constant; duration adjusts with resource unit changes |
FixedDuration | Duration stays fixed; work adjusts with resource unit changes |
FixedUnit | Resource units stay constant; duration or work adjusts |
Data Source Example
const data = [
{
TaskID: 1,
TaskName: 'Development',
StartDate: new Date('04/02/2024'),
Duration: 5,
Work: 40, // 40 hours of effort
resources: [{ resourceId: 1, unit: 100 }],
ParentID: null,
},
];---
Timezone
Set an IANA timezone string on the Gantt to ensure consistent date display across users in different regions.
<GanttComponent
timezone='UTC'
timelineSettings={{
topTier: { unit: 'Day' },
bottomTier: { unit: 'Hour' },
}}
dataSource={data}
taskFields={taskFields}
height='450px'
/>Thetimezoneprop has visible effect only when the timeline renders hours. UsetimelineViewMode='Hour'or configure an hourly bottom tier.
Common Timezone Values
| Value | Description |
|---|---|
'UTC' | Coordinated Universal Time |
'America/New_York' | US Eastern Time |
'America/Los_Angeles' | US Pacific Time |
'Europe/London' | UK Time |
'Asia/Kolkata' | India Standard Time |
'Asia/Tokyo' | Japan Standard Time |
---
Common Pitfalls
| Issue | Cause | Fix |
|---|---|---|
| Holidays not rendering | DayMarkers service not injected | Add <Inject services={[DayMarkers]} /> |
| Holiday styling not applied | Wrong CSS selector | Target .{cssClass} .e-span-label for label text styling |
| Weekend days still blocked | includeWeekend not set | Set includeWeekend={true} |
| Work field has no effect | taskFields.work not mapped | Add work: 'Work' to taskFields |
| Timezone has no visible effect | Timeline not showing hours | Set timelineViewMode='Hour' or configure hourly bottom tier |
| Duration calculation wrong | dayWorkingTime not set | Configure dayWorkingTime to match actual working hours |
Localization and RTL in Syncfusion React Gantt Chart
Table of Contents
- Overview
- Setting the Locale
- Loading Custom Translations
- Gantt Locale Keys Reference
- RTL (Right-to-Left) Support
- Column Number and Date Formatting
- Common Pitfalls
---
Overview
The Gantt component supports localization (translating built-in UI text) and RTL layout for global deployments. These are controlled via:
localeprop — sets the active language/cultureL10n.load()— registers translation strings for the'gantt'namespaceenableRtlprop — flips the component layout direction
---
Setting the Locale
Pass the locale string to the locale prop on GanttComponent:
<GanttComponent
locale='de'
dataSource={data}
taskFields={taskFields}
height='450px'
/>Built-in locale strings for many languages are available from the@syncfusion/ej2-localepackage. For custom translations, useL10n.load().
---
Loading Custom Translations
Use L10n.load() from @syncfusion/ej2-base to register translation strings before the component renders. Strings are scoped to the 'gantt' namespace:
import { L10n } from '@syncfusion/ej2-base';
// Call this BEFORE the component renders (e.g., at the top of your module)
L10n.load({
'de': {
'gantt': {
'emptyRecord': 'Keine Daten vorhanden',
'id': 'ID',
'name': 'Name',
'startDate': 'Startdatum',
'endDate': 'Enddatum',
'duration': 'Dauer',
'progress': 'Fortschritt',
'dependency': 'Abhängigkeit',
'notes': 'Notizen',
'addDialogTitle': 'Neue Aufgabe',
'editDialogTitle': 'Aufgabe bearbeiten',
'saveButton': 'Speichern',
'cancelButton': 'Abbrechen',
'deleteButton': 'Löschen',
'add': 'Hinzufügen',
'edit': 'Bearbeiten',
'update': 'Aktualisieren',
'delete': 'Löschen',
'cancel': 'Abbrechen',
'search': 'Suchen',
'task': 'Aufgabe',
'tasks': 'Aufgaben',
'expandAll': 'Alle erweitern',
'collapseAll': 'Alle reduzieren',
'zoomIn': 'Vergrößern',
'zoomOut': 'Verkleinern',
'zoomToFit': 'Anpassen',
'excelExport': 'Excel-Export',
'csvExport': 'CSV-Export',
'pdfExport': 'PDF-Export',
'okText': 'OK',
'confirmDelete': 'Möchten Sie den Datensatz wirklich löschen?',
'from': 'Von',
'to': 'Bis',
'taskLink': 'Aufgabenverknüpfung',
'lag': 'Verzögerung',
'start': 'Start',
'finish': 'Ende',
'enterValue': 'Wert eingeben',
'taskBeforePredecessor_FS': 'Sie haben "{0}" verschoben, bevor "{1}" fertig ist',
'taskAfterPredecessor_FS': 'Sie haben "{0}" von "{1}" weg verschoben',
}
}
});
function App() {
return (
<GanttComponent
locale='de'
dataSource={data}
taskFields={taskFields}
height='450px'
/>
);
}L10n.load() must be called before the component mounts. Place it at the module level (outside the component function), or in a bootstrap/setup file.---
Gantt Locale Keys Reference
These are the most commonly used keys in the 'gantt' namespace:
| Key | Default (English) | Description |
|---|---|---|
emptyRecord | 'No records to display' | Empty data message |
id | 'ID' | Task ID column header |
name | 'Name' | Task Name column header |
startDate | 'Start Date' | Start Date column header |
endDate | 'End Date' | End Date column header |
duration | 'Duration' | Duration column header |
progress | 'Progress' | Progress column header |
dependency | 'Dependency' | Dependency column header |
notes | 'Notes' | Notes column header |
addDialogTitle | 'New Task' | Add dialog title |
editDialogTitle | 'Task Information' | Edit dialog title |
saveButton | 'Save' | Save button label |
cancelButton | 'Cancel' | Cancel button label |
deleteButton | 'Delete' | Delete button label |
add | 'Add' | Toolbar Add button |
edit | 'Edit' | Toolbar Edit button |
update | 'Update' | Toolbar Update button |
delete | 'Delete' | Toolbar Delete button |
cancel | 'Cancel' | Toolbar Cancel button |
search | 'Search' | Toolbar Search placeholder |
expandAll | 'Expand All' | Toolbar Expand All button |
collapseAll | 'Collapse All' | Toolbar Collapse All button |
zoomIn | 'Zoom In' | Toolbar Zoom In button |
zoomOut | 'Zoom Out' | Toolbar Zoom Out button |
zoomToFit | 'Zoom To Fit' | Toolbar Zoom to Fit button |
excelExport | 'Excel Export' | Toolbar Excel Export button |
csvExport | 'CSV Export' | Toolbar CSV Export button |
pdfExport | 'PDF Export' | Toolbar PDF Export button |
confirmDelete | 'Are you sure you want to Delete Record?' | Delete confirmation dialog |
from | 'From' | Dependency dialog From label |
to | 'To' | Dependency dialog To label |
taskLink | 'Task Link' | Dependency dialog title |
lag | 'Lag' | Dependency dialog Lag label |
start | 'Start' | Constraint start label |
finish | 'Finish' | Constraint finish label |
---
RTL (Right-to-Left) Support
Enable RTL layout for Arabic, Hebrew, or other right-to-left languages:
<GanttComponent
enableRtl={true}
locale='ar'
dataSource={data}
taskFields={taskFields}
height='450px'
/>Set bothenableRtl={true}andlocale='ar'(or your target RTL locale) together for the full RTL experience — layout direction and translated UI text.
What RTL Affects
- TreeGrid columns render right-to-left
- Column headers are right-aligned
- Toolbar items order is reversed
- Dialog fields are right-aligned
- Scrollbar appears on the left side
- Timeline reads right-to-left
---
Column Number and Date Formatting
Apply display formats per column using the format prop on ColumnDirective:
Date Formatting
<ColumnDirective field='StartDate' format='yMd' /> {/* locale-aware short date */}
<ColumnDirective field='StartDate' format='dd/MM/yyyy' /> {/* custom pattern */}
<ColumnDirective field='StartDate' format={{ type: 'date', skeleton: 'full' }} />Number Formatting
<ColumnDirective field='Cost' format='C2' /> {/* currency, 2 decimals: $1,234.56 */}
<ColumnDirective field='Progress' format='P0' /> {/* percentage, 0 decimals: 75% */}
<ColumnDirective field='Rate' format='N2' /> {/* number, 2 decimals: 1,234.56 */}---
Common Pitfalls
| Issue | Cause | Fix |
|---|---|---|
| Translations not applied | L10n.load() called after component renders | Call L10n.load() at the module level before the component mounts |
| Partial translation | Missing keys in locale object | Add all required keys to the 'gantt' namespace |
| RTL layout not applied | enableRtl not set | Set enableRtl={true} alongside locale |
| Date format not changing | format not set on ColumnDirective | Add format='yMd' or a custom pattern to the date column |
| Number format ignored | Wrong format string | Use EJ2 format strings: 'C2', 'N2', 'P0' etc. |
Rows in Syncfusion React Gantt Chart
Table of Contents
---
Row Drag and Drop
Enable drag-and-drop to reorder or reparent tasks:
import { RowDD, Edit, Selection } from '@syncfusion/ej2-react-gantt';
<GanttComponent
allowRowDragAndDrop={true}
editSettings={{ allowEditing: true }}
...
>
<Inject services={[RowDD, Edit, Selection]} />
</GanttComponent>What it does:
- Drag a row to reorder among siblings
- Drag onto a parent task to reparent (make a child of the target)
- Dropping between rows creates a sibling; dropping onto a task makes it a child
Drag multiple rows:
<GanttComponent allowRowDragAndDrop={true} selectionSettings={{ type: 'Multiple' }} ...>
<Inject services={[RowDD, Edit, Selection]} />
</GanttComponent>Using toolbar for indent/outdent during drag:
toolbar={['Indent', 'Outdent']}---
Indent and Outdent
Indent moves a task down one level (becomes a child of the task above it). Outdent moves a task up one level (becomes a sibling of its parent).
Enable via toolbar buttons:
<GanttComponent toolbar={['Indent', 'Outdent']} editSettings={{ allowEditing: true }} ...>
<Inject services={[Edit, Toolbar]} />
</GanttComponent>Programmatic indent/outdent:
const ganttRef = React.useRef<GanttComponent>(null);
ganttRef.current?.indent(); // indent selected task
ganttRef.current?.outdent(); // outdent selected taskThe task above the indented row automatically becomes the parent. If there is no task above, indent is not allowed.
---
Row Spanning
Row spanning allows grid cells to merge across multiple rows, useful for displaying shared labels without duplication. Use the queryCellInfo event and set args.rowSpan:
import { QueryCellInfoEventArgs } from '@syncfusion/ej2-react-gantt';
function queryCellInfo(args: QueryCellInfoEventArgs) {
// Span TaskID 4's TaskName cell across 2 rows
if ((args.data as any).TaskID === 4 && args.column.field === 'TaskName') {
args.rowSpan = 2;
}
}
<GanttComponent
dataSource={data}
taskFields={taskFields}
gridLines="Both"
queryCellInfo={queryCellInfo}
height="400px"
/>`queryCellInfo` args properties:
| Property | Description |
|---|---|
args.data | Row data object |
args.column | Column definition object (field, headerText, etc.) |
args.rowSpan | Number of rows this cell should span (vertical merge) |
args.colSpan | Number of columns this cell should span (horizontal merge) |
args.cell | The actual DOM <td> element |
Combining rowSpan and colSpan:
function queryCellInfo(args: QueryCellInfoEventArgs) {
if ((args.data as any).TaskID === 2 && args.column.field === 'StartDate') {
args.rowSpan = 2; // span 2 rows
args.colSpan = 2; // span 2 columns simultaneously
}
}Row spanning does not affect the Gantt chart panel — only the TreeGrid (left-side) cells are affected.
---
Row Height
Set a uniform row height:
<GanttComponent rowHeight={40} ... /> {/* pixels */}---
Row Styling
Customize row appearance per-row using rowDataBound:
function rowDataBound(args: any) {
if (args.data.Progress < 50) {
args.row.style.backgroundColor = '#fff3cd'; // yellow highlight for low progress
}
if (args.data.TaskName === 'Critical Milestone') {
args.row.style.fontWeight = 'bold';
}
}
<GanttComponent rowDataBound={rowDataBound} .../>---
Expand and Collapse Rows
Toolbar buttons:
<GanttComponent toolbar={['ExpandAll', 'CollapseAll']} ...>
<Inject services={[Toolbar]} />
</GanttComponent>Programmatic control:
const ganttRef = React.useRef<GanttComponent>(null);
ganttRef.current?.expandAll(); // expand all rows
ganttRef.current?.collapseAll(); // collapse all rows
ganttRef.current?.expandByID(1); // expand row with TaskID = 1
ganttRef.current?.collapseByID(5); // collapse row with TaskID = 5
ganttRef.current?.expandAtLevel(2); // expand rows to depth level 2Related skills
How it compares
Choose this over generic React UI skills when you need Syncfusion-specific GanttComponent APIs, exports, and enterprise scheduling features.
FAQ
What does syncfusion-react-gantt-chart help implement?
syncfusion-react-gantt-chart teaches GanttComponent setup for React project scheduling: task timelines, FS/SS/FF/SF dependencies, resource views, filtering, Excel/PDF export, critical path, and milestones using Syncfusion version 33.1.44 APIs.
How do you install syncfusion-react-gantt-chart?
Run npx skills add syncfusion/react-ui-components-skills --skill syncfusion-react-gantt-chart. The skill loads SKILL.md plus 27 references covering data binding, scheduling modes, exports, and localization for accurate agent-generated React code.