Column spanning in React Gantt component
19 Nov 202524 minutes to read
The Syncfusion® React Gantt component supports column spanning, allowing adjacent cells to merge horizontally for improved layout clarity. This feature is useful for grouping related data or enhancing visual structure.
To enable column spanning, use the queryCellInfo event and set the colSpan property to define how many columns a cell should span.
In the following example, Work cells are spanned to improve visual clarity:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, ColumnsDirective, ColumnDirective } from '@syncfusion/ej2-react-gantt';
import { GanttData } from './datasource';
function App() {
const queryCellInfoEvent = (args) => {
const field = args.column.field;
switch (args.data.TaskID) {
case 1:
if (field === 'work3' || field === 'work4') {
args.colSpan = 1;
} else if (field === 'work1') {
args.colSpan = 2;
}
break;
case 2:
if (field === 'work1' || field === 'work4') {
args.colSpan = 1;
} else if (field === 'work2') {
args.colSpan = 2;
}
break;
case 3:
if (field === 'work4') {
args.colSpan = 1;
} else if (field === 'work1') {
args.colSpan = 3;
}
break;
case 4:
if (field === 'work4' || field === 'work1') {
args.colSpan = 1;
}
break;
case 5:
if (field === 'work3') {
args.colSpan = 2;
} else if (field === 'work1') {
args.colSpan = 2;
}
break;
case 6:
if (field === 'work4') {
args.colSpan = 1;
} else if (field === 'work1') {
args.colSpan = 3;
}
break;
case 7:
if (field === 'work4' || field === 'work1') {
args.colSpan = 1;
}
break;
case 8:
if (field === 'work1' || field === 'work4') {
args.colSpan = 1;
} else if (field === 'work2') {
args.colSpan = 2;
}
break;
}
}
const taskFields = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
progress: 'Progress',
parentID: 'ParentID',
work1: 'work1',
work2: 'work2',
};
const splitterSettings = {
position: '75%'
};
return (<div> <GanttComponent
dataSource={GanttData}
taskFields={taskFields}
splitterSettings={splitterSettings}
gridLines="Both"
height="430px"
queryCellInfo={queryCellInfoEvent}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" textAlign="Right" width="90" />
<ColumnDirective field="TaskName" headerText="Task Name" textAlign="Left" width="290" />
<ColumnDirective field="work1" headerText="Work 1" textAlign="Center" width="150" />
<ColumnDirective field="work2" headerText="Work 2" textAlign="Center" width="150" />
<ColumnDirective field="work3" headerText="Work 3" textAlign="Center" width="150" />
<ColumnDirective field="work4" headerText="Work 4" textAlign="Center" width="150" />
<ColumnDirective field="StartDate" headerText="Start Date" textAlign="Right" width="120" />
<ColumnDirective field="Duration" headerText="Duration" textAlign="Right" width="90" />
<ColumnDirective field="Progress" headerText="Progress" textAlign="Right" width="120" />
</ColumnsDirective>
</GanttComponent></div>)
};
ReactDOM.render(<App />, document.getElementById('root'));import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, ColumnsDirective, ColumnDirective } from '@syncfusion/ej2-react-gantt';
import { GanttData } from './datasource';
import { QueryCellInfoEventArgs } from '@syncfusion/ej2-react-gantt';
function App() {
const queryCellInfoEvent = (args: QueryCellInfoEventArgs) => {
const field = args.column.field;
switch (args.data.TaskID) {
case 1:
if (field === 'work3' || field === 'work4') {
args.colSpan = 1;
} else if (field === 'work1') {
args.colSpan = 2;
}
break;
case 2:
if (field === 'work1' || field === 'work4') {
args.colSpan = 1;
} else if (field === 'work2') {
args.colSpan = 2;
}
break;
case 3:
if (field === 'work4') {
args.colSpan = 1;
} else if (field === 'work1') {
args.colSpan = 3;
}
break;
case 4:
if (field === 'work4' || field === 'work1') {
args.colSpan = 1;
}
break;
case 5:
if (field === 'work3') {
args.colSpan = 2;
} else if (field === 'work1') {
args.colSpan = 2;
}
break;
case 6:
if (field === 'work4') {
args.colSpan = 1;
} else if (field === 'work1') {
args.colSpan = 3;
}
break;
case 7:
if (field === 'work4' || field === 'work1') {
args.colSpan = 1;
}
break;
case 8:
if (field === 'work1' || field === 'work4') {
args.colSpan = 1;
} else if (field === 'work2') {
args.colSpan = 2;
}
break;
}
}
let ganttInstance: any;
const taskFields: any = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
progress: 'Progress',
parentID: 'ParentID',
work1: 'work1',
work2: 'work2',
};
const splitterSettings: any = {
position: '75%'
};
return (<div> <GanttComponent
dataSource={GanttData}
taskFields={taskFields}
splitterSettings={splitterSettings}
gridLines="Both"
height="430px"
queryCellInfo={queryCellInfoEvent}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" textAlign="Right" width="90" />
<ColumnDirective field="TaskName" headerText="Task Name" textAlign="Left" width="290" />
<ColumnDirective field="work1" headerText="Work 1" textAlign="Center" width="150" />
<ColumnDirective field="work2" headerText="Work 2" textAlign="Center" width="150" />
<ColumnDirective field="work3" headerText="Work 3" textAlign="Center" width="150" />
<ColumnDirective field="work4" headerText="Work 4" textAlign="Center" width="150" />
<ColumnDirective field="StartDate" headerText="Start Date" textAlign="Right" width="120" />
<ColumnDirective field="Duration" headerText="Duration" textAlign="Right" width="90" />
<ColumnDirective field="Progress" headerText="Progress" textAlign="Right" width="120" />
</ColumnsDirective>
</GanttComponent></div>)
};
ReactDOM.render(<App />, document.getElementById('root'));<!DOCTYPE html>
<html lang="en">
<head>
<title>Syncfusion React Gantt</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Essential JS 2 for React Components" />
<meta name="author" content="Syncfusion" />
<link href="https://cdn.syncfusion.com/ej2/32.1.19/material.css" rel="stylesheet" type="text/css"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<script src="systemjs.config.js"></script>
<style>
#loader {
color: #008cff;
height: 40px;
left: 45%;
position: absolute;
top: 45%;
width: 30%;
}
</style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='root'>
<div id='loader'>Loading....</div>
</div>
</body>
</html>Customize border color of spanned columns
You can customize the border color of spanned cells using the queryCellInfo event. This event triggers before the cell is rendered, allowing you to apply custom styles dynamically.
The following example demonstrates how to change the border color of spanned cells:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, ColumnsDirective, ColumnDirective } from '@syncfusion/ej2-react-gantt';
import { GanttData } from './datasource';
function App() {
const queryCellInfoEvent = (args) => {
const field = args.column.field;
switch (args.data.TaskID) {
case 1:
if (field === 'work3' || field === 'work4') {
args.colSpan = 1;
} else if (field === 'work1') {
args.colSpan = 2;
}
break;
case 2:
if (field === 'work1' || field === 'work4') {
args.colSpan = 1;
} else if (field === 'work2') {
args.colSpan = 2;
}
break;
case 3:
if (field === 'work4') {
args.colSpan = 1;
} else if (field === 'work1') {
args.colSpan = 3;
}
break;
case 4:
if (field === 'work4' || field === 'work1') {
args.colSpan = 1;
}
break;
case 5:
if (field === 'work3') {
args.colSpan = 2;
} else if (field === 'work1') {
args.colSpan = 2;
}
break;
case 6:
if (field === 'work4') {
args.colSpan = 1;
} else if (field === 'work1') {
args.colSpan = 3;
}
break;
case 7:
if (field === 'work4' || field === 'work1') {
args.colSpan = 1;
}
break;
case 8:
if (field === 'work1' || field === 'work4') {
args.colSpan = 1;
} else if (field === 'work2') {
args.colSpan = 2;
}
break;
}
if (args.colSpan > 1) {
args.cell.style.border = '1px solid red';
}
}
const taskFields = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
progress: 'Progress',
parentID: 'ParentID',
work1: 'work1',
work2: 'work2',
};
const splitterSettings = {
position: '75%'
};
return (<div> <GanttComponent
dataSource={GanttData}
taskFields={taskFields}
splitterSettings={splitterSettings}
gridLines="Both"
height="430px"
queryCellInfo={queryCellInfoEvent}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" textAlign="Right" width="90" />
<ColumnDirective field="TaskName" headerText="Task Name" textAlign="Left" width="290" />
<ColumnDirective field="work1" headerText="Work 1" textAlign="Center" width="150" />
<ColumnDirective field="work2" headerText="Work 2" textAlign="Center" width="150" />
<ColumnDirective field="work3" headerText="Work 3" textAlign="Center" width="150" />
<ColumnDirective field="work4" headerText="Work 4" textAlign="Center" width="150" />
<ColumnDirective field="StartDate" headerText="Start Date" textAlign="Right" width="120" />
<ColumnDirective field="Duration" headerText="Duration" textAlign="Right" width="90" />
<ColumnDirective field="Progress" headerText="Progress" textAlign="Right" width="120" />
</ColumnsDirective>
</GanttComponent></div>)
};
ReactDOM.render(<App />, document.getElementById('root'));import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, ColumnsDirective, ColumnDirective } from '@syncfusion/ej2-react-gantt';
import { GanttData } from './datasource';
import { QueryCellInfoEventArgs } from '@syncfusion/ej2-react-gantt';
function App() {
const queryCellInfoEvent = (args: QueryCellInfoEventArgs) => {
const field = args.column.field;
switch (args.data.TaskID) {
case 1:
if (field === 'work3' || field === 'work4') {
args.colSpan = 1;
} else if (field === 'work1') {
args.colSpan = 2;
}
break;
case 2:
if (field === 'work1' || field === 'work4') {
args.colSpan = 1;
} else if (field === 'work2') {
args.colSpan = 2;
}
break;
case 3:
if (field === 'work4') {
args.colSpan = 1;
} else if (field === 'work1') {
args.colSpan = 3;
}
break;
case 4:
if (field === 'work4' || field === 'work1') {
args.colSpan = 1;
}
break;
case 5:
if (field === 'work3') {
args.colSpan = 2;
} else if (field === 'work1') {
args.colSpan = 2;
}
break;
case 6:
if (field === 'work4') {
args.colSpan = 1;
} else if (field === 'work1') {
args.colSpan = 3;
}
break;
case 7:
if (field === 'work4' || field === 'work1') {
args.colSpan = 1;
}
break;
case 8:
if (field === 'work1' || field === 'work4') {
args.colSpan = 1;
} else if (field === 'work2') {
args.colSpan = 2;
}
break;
}
if (args.colSpan > 1) {
(args.cell as HTMLElement).style.border = '1px solid red';
}
}
const taskFields: any = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
progress: 'Progress',
parentID: 'ParentID',
work1: 'work1',
work2: 'work2',
};
const splitterSettings: any = {
position: '75%'
};
return (<div> <GanttComponent
dataSource={GanttData}
taskFields={taskFields}
splitterSettings={splitterSettings}
gridLines="Both"
height="430px"
queryCellInfo={queryCellInfoEvent}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" textAlign="Right" width="90" />
<ColumnDirective field="TaskName" headerText="Task Name" textAlign="Left" width="290" />
<ColumnDirective field="work1" headerText="Work 1" textAlign="Center" width="150" />
<ColumnDirective field="work2" headerText="Work 2" textAlign="Center" width="150" />
<ColumnDirective field="work3" headerText="Work 3" textAlign="Center" width="150" />
<ColumnDirective field="work4" headerText="Work 4" textAlign="Center" width="150" />
<ColumnDirective field="StartDate" headerText="Start Date" textAlign="Right" width="120" />
<ColumnDirective field="Duration" headerText="Duration" textAlign="Right" width="90" />
<ColumnDirective field="Progress" headerText="Progress" textAlign="Right" width="120" />
</ColumnsDirective>
</GanttComponent></div>)
};
ReactDOM.render(<App />, document.getElementById('root'));<!DOCTYPE html>
<html lang="en">
<head>
<title>Syncfusion React Gantt</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Essential JS 2 for React Components" />
<meta name="author" content="Syncfusion" />
<link href="https://cdn.syncfusion.com/ej2/32.1.19/material.css" rel="stylesheet" type="text/css"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<script src="systemjs.config.js"></script>
<style>
#loader {
color: #008cff;
height: 40px;
left: 45%;
position: absolute;
top: 45%;
width: 30%;
}
</style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='root'>
<div id='loader'>Loading....</div>
</div>
</body>
</html>Limitations
Column spanning is not compatible with the following features:
- Virtual scrolling
- Infinite scrolling
Ensure these features are disabled when using column spanning to avoid rendering issues.