Checkbox Selection in React TreeGrid
Checkbox selection enables selecting multiple TreeGrid records using a checkbox in each row.
To render a checkbox in each TreeGrid row, add a column with the type property set to CheckBox.
import { ColumnDirective, ColumnsDirective, Inject, TreeGridComponent } from '@syncfusion/ej2-react-treegrid';
import { Page } from '@syncfusion/ej2-react-treegrid';
import * as React from 'react';
import { sampleData } from './datasource';
function App() {
return <TreeGridComponent dataSource={sampleData} treeColumnIndex={2} childMapping='subtasks' allowPaging={true}>
<ColumnsDirective>
<ColumnDirective type='checkbox' width='50'/>
<ColumnDirective field='taskID' headerText='Task ID' width='90' textAlign='Right'/>
<ColumnDirective field='taskName' headerText='Task Name' width='180'/>
<ColumnDirective field='startDate' headerText='Start Date' width='90' format='yMd' textAlign='Right' type='date'/>
<ColumnDirective field='duration' headerText='Duration' width='80' textAlign='Right'/>
</ColumnsDirective>
<Inject services={[Page]}/>
</TreeGridComponent>;
}
;
export default App;import { ColumnDirective, ColumnsDirective, Inject, TreeGridComponent } from '@syncfusion/ej2-react-treegrid';
import { Page } from '@syncfusion/ej2-react-treegrid';
import * as React from 'react';
import { sampleData } from './datasource';
function App() {
return <TreeGridComponent dataSource={sampleData} treeColumnIndex={2} childMapping='subtasks' allowPaging={true}>
<ColumnsDirective>
<ColumnDirective type='checkbox' width='50'/>
<ColumnDirective field='taskID' headerText='Task ID' width='90' textAlign='Right'/>
<ColumnDirective field='taskName' headerText='Task Name' width='180'/>
<ColumnDirective field='startDate' headerText='Start Date' width='90' format='yMd' textAlign='Right' type='date' />
<ColumnDirective field='duration' headerText='Duration' width='80' textAlign='Right' />
</ColumnsDirective>
<Inject services={[Page]}/>
</TreeGridComponent>
};
export default App;By default, selection is allowed by clicking a TreeGrid row or its checkbox. To allow selection only through checkboxes, set selectionSettings.checkboxOnly to “true”.
Selection can be persisted on all the operations using selectionSettings.persistSelection. To enable persistence, define at least one column as a primary key using columns.isPrimaryKey property.
Checkbox selection mode
In checkbox selection, rows can also be selected by clicking anywhere on the row. Configure the behavior using selectionSettings.checkboxMode. The modes are:
-
Default: Multiple rows can be selected by clicking rows one by one. -
ResetOnRowClick: Clicking a row clears the previously selected rows. Multiple selection is still available by holding CTRL and clicking additional rows. To select a contiguous range, hold SHIFT and click the end row.
import { ColumnDirective, ColumnsDirective, Inject, TreeGridComponent } from '@syncfusion/ej2-react-treegrid';
import { Page } from '@syncfusion/ej2-react-treegrid';
import * as React from 'react';
import { sampleData } from './datasource';
function App() {
const settings = { checkboxMode: 'ResetOnRowClick' };
return <TreeGridComponent dataSource={sampleData} treeColumnIndex={2} childMapping='subtasks' allowPaging={true} selectionSettings={settings}>
<ColumnsDirective>
<ColumnDirective type='checkbox' width='50'/>
<ColumnDirective field='taskID' headerText='Task ID' width='90' textAlign='Right'/>
<ColumnDirective field='taskName' headerText='Task Name' width='180'/>
<ColumnDirective field='startDate' headerText='Start Date' width='90' format='yMd' textAlign='Right' type='date'/>
<ColumnDirective field='duration' headerText='Duration' width='80' textAlign='Right'/>
</ColumnsDirective>
<Inject services={[Page]}/>
</TreeGridComponent>;
}
;
export default App;import { ColumnDirective, ColumnsDirective, Inject, TreeGridComponent } from '@syncfusion/ej2-react-treegrid';
import { Page, SelectionSettingsModel } from '@syncfusion/ej2-react-treegrid';
import * as React from 'react';
import { sampleData } from './datasource';
function App() {
const settings: SelectionSettingsModel = { checkboxMode: 'ResetOnRowClick'};
return <TreeGridComponent dataSource={sampleData} treeColumnIndex={2} childMapping='subtasks' allowPaging={true} selectionSettings={settings}>
<ColumnsDirective>
<ColumnDirective type='checkbox' width='50'/>
<ColumnDirective field='taskID' headerText='Task ID' width='90' textAlign='Right'/>
<ColumnDirective field='taskName' headerText='Task Name' width='180'/>
<ColumnDirective field='startDate' headerText='Start Date' width='90' format='yMd' textAlign='Right' type='date' />
<ColumnDirective field='duration' headerText='Duration' width='80' textAlign='Right' />
</ColumnsDirective>
<Inject services={[Page]}/>
</TreeGridComponent>
};
export default App;Checkbox selection applies to row selection only and is not compatible with cell selection mode.
Conditional row selection
The TreeGrid supports conditional row selection through the isRowSelectable callback. This allows selection to be controlled by custom business logic, ensuring that only rows meeting specific conditions can be selected. The callback receives each row’s data and returns “true” to allow selection or “false” to prevent it.
Local data: The callback runs once when the TreeGrid initializes and evaluates all records because the full dataset is already available on the client.
Remote data: The callback runs only for the rows displayed on the current page when the TreeGrid first loads. It runs again whenever the TreeGrid fetches new data such as during paging, filtering, or sorting to re-evaluate the newly visible rows.
In the following sample, selection is disabled for rows where the “Progress” column has the value “Completed”.
import {
TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, Selection, VirtualScroll,
} from '@syncfusion/ej2-react-treegrid';
import { taskData } from './datasource';
import * as React from 'react';
function App() {
let treegridInstance;
const selectionSettings = { persistSelection: true };
const isRowSelectable = (data,columns) => {
return data.Progress !== 'Completed';
};
return <TreeGridComponent
id="TreeGrid"
ref={(treegrid) => { treegridInstance = treegrid }}
dataSource={taskData}
idMapping='TaskID'
parentIdMapping='ParentID'
treeColumnIndex={1}
height={600}
enableVirtualization={true}
selectionSettings={selectionSettings}
isRowSelectable= {isRowSelectable}
>
<ColumnsDirective>
<ColumnDirective type="checkbox" width="50" />
<ColumnDirective field="Task" headerText="Task" width="300" />
<ColumnDirective field="TaskID" isPrimaryKey={true} visible={false} />
<ColumnDirective field="AssignedTo" headerText="Assigned To" width="140" />
<ColumnDirective field="StartDate" headerText="Start" format='yMd' width="180" />
<ColumnDirective field="DueDate" headerText="Due" format='yMd' width="180" />
<ColumnDirective field="Priority" headerText="Priority" width="90" />
<ColumnDirective field="Progress" headerText="Status" width="110" />
</ColumnsDirective>
<Inject services={[Selection,VirtualScroll]} />
</TreeGridComponent>
}
;
export default App;import {
TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, Selection, VirtualScroll,
} from '@syncfusion/ej2-react-treegrid';
import { taskData } from './datasource';
import * as React from 'react';
function App() {
let treegridcs10: TreeGridComponent;
const selectionSettings: any = { persistSelection: true };
const isRowSelectable = (data: any,columns:any): boolean => {
return data.Progress !== 'Completed';
};
return <TreeGridComponent
id="TreeGrid"
ref={(treegrid) => { treegridcs10 = treegrid }}
dataSource={taskData}
idMapping='TaskID'
parentIdMapping='ParentID'
treeColumnIndex={1}
height={600}
enableVirtualization={true}
selectionSettings={selectionSettings}
isRowSelectable={isRowSelectable}
>
<ColumnsDirective>
<ColumnDirective type="checkbox" width="50" />
<ColumnDirective field="Task" headerText="Task" width="300" />
<ColumnDirective field="TaskID" isPrimaryKey={true} visible={false} />
<ColumnDirective field="AssignedTo" headerText="Assigned To" width="140" />
<ColumnDirective field="StartDate" headerText="Start" format='yMd' width="180" />
<ColumnDirective field="DueDate" headerText="Due" format='yMd' width="180" />
<ColumnDirective field="Priority" headerText="Priority" width="90" />
<ColumnDirective field="Progress" headerText="Status" width="110" />
</ColumnsDirective>
<Inject services={[Selection, VirtualScroll]} />
</TreeGridComponent>
}
;
export default App;Hierarchy checkbox selection
The hierarchy checkbox enables cascading selection of parent and child rows in Tree Grid. When a parent row checkbox is selected, all its child records are automatically selected, and the parent state updates based on children selection. To enable hierarchy checkbox selection, set the showCheckbox property to true in a column and configure the hierarchyCheckboxMode property. By default, hierarchy checkbox mode is set to self.
The available modes are displayed in the following table.
| Item | Description |
|---|---|
self |
Only the targeted row is selected; children and parent are not affected. |
hierarchy |
Selection cascades to all descendants and adjusts the parent state accordingly. |
filteredHierarchy |
Behaves like hierarchy, but applies the cascade only to records matching the current filter or search criteria. |
import * as React from 'react';
import { useEffect, useState } from 'react';
import { ColumnDirective, ColumnsDirective, Filter, Inject, Toolbar, Edit, TreeGridComponent } from '@syncfusion/ej2-react-treegrid';
import { DropDownListComponent } from '@syncfusion/ej2-react-dropdowns';
import { showCheckBoxData } from './data';
const SAMPLE_CSS = `
.checkboxprop {
padding-bottom: 8px;
}
/* Outer wrapper containing label and dropdown */
.checkboxprop > .checkboxmode {
display: flex;
align-items: center;
gap: 12px;
}
/* Label */
.checkboxprop > .checkboxmode > span {
display: inline-block;
white-space: nowrap;
font-size: 14px;
}
/* Inner dropdown wrapper */
.checkboxprop > .checkboxmode > .checkboxmode {
display: block;
width: 180px;
flex: 0 0 180px;
}
/* Ensure the Syncfusion dropdown uses the wrapper width */
.checkboxprop .e-dropdownlist {
width: 100% !important;
}
.e-bigger.bootstrap5 .e-treegrid .e-hierarchycheckbox .e-frame,
.e-bigger.bootstrap5-dark .e-treegrid .e-hierarchycheckbox .e-frame,
.e-bigger.bootstrap4 .e-treegrid .e-hierarchycheckbox .e-frame {
height: 17px;
width: 17px;
}
.checkboxprop {
padding-bottom: 5px;
}
.checkboxmode {
display: inline-block;
padding-right: 5px;
}
.status-badge {
padding: 4px 10px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
display: inline-block;
}
.completed {
background: #dcfce7;
color: #15803d;
}
.in-progress {
background: #dbeafe;
color: #1d4ed8;
}
.not-started {
background: #f3f4f6;
color: #4b5563;
}
.under-review {
background: #fef3c7;
color: #b45309;
}
.blocked {
background: #fee2e2;
color: #dc2626;
}
`;
const hierarchyModeData = [
{
id: 'Self',
name: 'Self'
},
{
id: 'Hierarchy',
name: 'Hierarchy'
},
{
id: 'FilteredHierarchy',
name: 'Filtered Hierarchy'
}
];
const hierarchyModeFields = {
text: 'name',
value: 'id'
};
const editSettings = {
allowDeleting: true,
};
function App() {
const [hierarchyCheckboxMode, setHierarchyCheckboxMode] = useState('self');
const hierarchyModeChange = (args) => {
if (args.value === 'Hierarchy') {
setHierarchyCheckboxMode('hierarchy');
}
else if (args.value === 'FilteredHierarchy') {
setHierarchyCheckboxMode('filteredHierarchy');
}
else {
setHierarchyCheckboxMode('self');
}
};
const queryCellInfo = (args) => {
if (args.column?.field !== 'status' ||
!args.cell) {
return;
}
const taskData = args.data;
const statusClass = taskData.status
.toLowerCase()
.replace(/\s+/g, '-');
args.cell.innerHTML = `
<span class="status-badge ${statusClass}">
${taskData.status}
</span>
`;
};
return (<div className="control-pane">
<style>{SAMPLE_CSS}</style>
<div className="control-section">
<div>
<div className="checkboxprop">
<div className="checkboxmode">
<span>
Hierarchy Checkbox Mode
</span>
<div className="checkboxmode">
<DropDownListComponent id="hierarchyModes" dataSource={hierarchyModeData} fields={hierarchyModeFields} value="Self" width="180px" change={hierarchyModeChange} htmlAttributes=/>
</div>
</div>
</div>
<TreeGridComponent id="TreeGrid" dataSource={showCheckBoxData} childMapping="subTasks" treeColumnIndex={1} toolbar={['Search', 'Delete']} allowFiltering={true} height={380} editSettings={editSettings} hierarchyCheckboxMode={hierarchyCheckboxMode} queryCellInfo={queryCellInfo} aria-label="Tree Grid">
<ColumnsDirective>
<ColumnDirective field="taskID" visible={false} isPrimaryKey={true}/>
<ColumnDirective field="taskName" headerText="Task Name" width={270} showCheckbox={true}/>
<ColumnDirective field="assignee" headerText="Employee" width={180}/>
<ColumnDirective field="designation" headerText="Designation" width={220}/>
<ColumnDirective field="priority" headerText="Priority" width={140}/>
<ColumnDirective field="status" headerText="Status" width={120} textAlign="Center"/>
<ColumnDirective field="progress" headerText="Progress" width={120} textAlign="Right"/>
</ColumnsDirective>
<Inject services={[
Filter,
Toolbar,
Edit
]}/>
</TreeGridComponent>
</div>
</div>
</div>);
}
;
export default App;import * as React from 'react';
import { useEffect, useState } from 'react';
import {
ColumnDirective,
ColumnsDirective,
Filter,
Inject,
Toolbar,
Edit,
TreeGridComponent
} from '@syncfusion/ej2-react-treegrid';
import { DropDownListComponent } from '@syncfusion/ej2-react-dropdowns';
import type { ChangeEventArgs } from '@syncfusion/ej2-dropdowns';
import type { QueryCellInfoEventArgs } from '@syncfusion/ej2-grids';
import { showCheckBoxData } from './data';
type HierarchyCheckboxMode =
| 'self'
| 'hierarchy'
| 'filteredHierarchy';
interface HierarchyModeItem {
[key: string]: string;
id: string;
name: string;
}
interface TaskData {
taskID: number;
taskName: string;
assignee: string;
designation: string;
priority: string;
status: string;
progress: string;
expanded?: boolean;
subTasks?: TaskData[];
}
const SAMPLE_CSS: string = `
.checkboxprop {
padding-bottom: 8px;
}
/* Outer wrapper containing label and dropdown */
.checkboxprop > .checkboxmode {
display: flex;
align-items: center;
gap: 12px;
}
/* Label */
.checkboxprop > .checkboxmode > span {
display: inline-block;
white-space: nowrap;
font-size: 14px;
}
/* Inner dropdown wrapper */
.checkboxprop > .checkboxmode > .checkboxmode {
display: block;
width: 180px;
flex: 0 0 180px;
}
/* Ensure the Syncfusion dropdown uses the wrapper width */
.checkboxprop .e-dropdownlist {
width: 100% !important;
}
.e-bigger.bootstrap5 .e-treegrid .e-hierarchycheckbox .e-frame,
.e-bigger.bootstrap5-dark .e-treegrid .e-hierarchycheckbox .e-frame,
.e-bigger.bootstrap4 .e-treegrid .e-hierarchycheckbox .e-frame {
height: 17px;
width: 17px;
}
.checkboxprop {
padding-bottom: 5px;
}
.checkboxmode {
display: inline-block;
padding-right: 5px;
}
.status-badge {
padding: 4px 10px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
display: inline-block;
}
.completed {
background: #dcfce7;
color: #15803d;
}
.in-progress {
background: #dbeafe;
color: #1d4ed8;
}
.not-started {
background: #f3f4f6;
color: #4b5563;
}
.under-review {
background: #fef3c7;
color: #b45309;
}
.blocked {
background: #fee2e2;
color: #dc2626;
}
`;
const hierarchyModeData: HierarchyModeItem[] = [
{
id: 'Self',
name: 'Self'
},
{
id: 'Hierarchy',
name: 'Hierarchy'
},
{
id: 'FilteredHierarchy',
name: 'Filtered Hierarchy'
}
];
const hierarchyModeFields: {
text: string;
value: string;
} = {
text: 'name',
value: 'id'
};
const editSettings: any = {
allowDeleting: true,
};
function App() {
const [
hierarchyCheckboxMode,
setHierarchyCheckboxMode
] = useState<HierarchyCheckboxMode>('self');
const hierarchyModeChange = (
args: ChangeEventArgs
): void => {
if (args.value === 'Hierarchy') {
setHierarchyCheckboxMode('hierarchy');
} else if (args.value === 'FilteredHierarchy') {
setHierarchyCheckboxMode('filteredHierarchy');
} else {
setHierarchyCheckboxMode('self');
}
};
const queryCellInfo = (
args: QueryCellInfoEventArgs
): void => {
if (
args.column?.field !== 'status' ||
!args.cell
) {
return;
}
const taskData: TaskData = args.data as TaskData;
const statusClass: string = taskData.status
.toLowerCase()
.replace(/\s+/g, '-');
args.cell.innerHTML = `
<span class="status-badge ${statusClass}">
${taskData.status}
</span>
`;
};
return (
<div className="control-pane">
<style>{SAMPLE_CSS}</style>
<div className="control-section">
<div>
<div className="checkboxprop">
<div className="checkboxmode">
<span>
Hierarchy Checkbox Mode
</span>
<div className="checkboxmode">
<DropDownListComponent
id="hierarchyModes"
dataSource={hierarchyModeData}
fields={hierarchyModeFields}
value="Self"
width="180px"
change={hierarchyModeChange}
htmlAttributes=
/>
</div>
</div>
</div>
<TreeGridComponent
id="TreeGrid"
dataSource={
showCheckBoxData as TaskData[]
}
childMapping="subTasks"
treeColumnIndex={1}
toolbar={['Search', 'Delete']}
allowFiltering={true}
height={380}
editSettings={editSettings}
hierarchyCheckboxMode={
hierarchyCheckboxMode
}
queryCellInfo={queryCellInfo}
aria-label="Tree Grid"
>
<ColumnsDirective>
<ColumnDirective
field="taskID"
visible={false}
isPrimaryKey={true}
/>
<ColumnDirective
field="taskName"
headerText="Task Name"
width={270}
showCheckbox={true}
/>
<ColumnDirective
field="assignee"
headerText="Employee"
width={180}
/>
<ColumnDirective
field="designation"
headerText="Designation"
width={220}
/>
<ColumnDirective
field="priority"
headerText="Priority"
width={140}
/>
<ColumnDirective
field="status"
headerText="Status"
width={120}
textAlign="Center"
/>
<ColumnDirective
field="progress"
headerText="Progress"
width={120}
textAlign="Right"
/>
</ColumnsDirective>
<Inject
services={[
Filter,
Toolbar,
Edit
]}
/>
</TreeGridComponent>
</div>
</div>
</div>
);
}
;
export default App;