
Syncfusion React Treegrid
- 414 installs
- 3 repo stars
- Updated July 28, 2026
- syncfusion/react-ui-components-skills
syncfusion-react-treegrid is a Claude agent skill that helps developers implement Syncfusion React TreeGrid components with hierarchical data binding, columns, and editing patterns for enterprise React dashboards and adm
About
syncfusion-react-treegrid is a syncfusion/react-ui-components-skills agent skill focused on the Syncfusion React TreeGrid component for hierarchical tabular data in enterprise dashboards and admin consoles. Developers reach for it when nested rows—organizations, file trees, BOMs, or category hierarchies—need expand-collapse navigation, inline editing, sorting, and filtering without building a custom tree table from scratch. The skill encodes Syncfusion-specific props, data-source shaping, column templates, and React integration patterns so agents generate idiomatic TreeGrid JSX instead of generic HTML tables. Use syncfusion-react-treegrid when a SaaS admin surface requires parent-child row relationships with keyboard navigation and virtualization options available in the Syncfusion suite. Pair the skill with other syncfusion/react-ui-components-skills modules when the same screen also needs charts, schedulers, or form controls from the shared component library.
- syncfusion-react-treegrid
Syncfusion React Treegrid by the numbers
- 414 all-time installs (skills.sh)
- +27 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #1,017 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/react-ui-components-skills --skill syncfusion-react-treegridAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 414 |
|---|---|
| repo stars | ★ 3 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/react-ui-components-skills ↗ |
How do you implement Syncfusion React TreeGrid hierarchies?
Use syncfusion-react-treegrid for development tasks
Who is it for?
React developers building enterprise admin UIs who need Syncfusion TreeGrid for nested hierarchical tables instead of custom tree-table implementations.
Skip if: Teams using TanStack Table, AG Grid, or plain HTML tables without Syncfusion licensing or component requirements.
When should I use this skill?
A developer asks to add Syncfusion TreeGrid, bind hierarchical data, or configure nested row expand-collapse in a React admin UI.
What you get
React TreeGrid component code, hierarchical data-source configuration, column definitions, and Syncfusion TreeGrid integration patterns.
- TreeGrid React component
- Hierarchical data binding config
- Column and template definitions
Files
Syncfusion React TreeGrid
A comprehensive skill for implementing and customizing Syncfusion's React TreeGrid component. TreeGrid visualizes self-referential hierarchical data in a tabular layout with expand/collapse functionality, enterprise features like virtual scrolling, and comprehensive export options.
Table of Contents
- When to Use This Skill
- TreeGrid Overview
- Data Structure Rules
- Documentation Navigation Guide
- Quick Start Example
When to Use This Skill
Use this skill when you need to:
- Display hierarchical or tree-structured data (organizational charts, file systems, bill of materials)
- Configure columns with proper data binding and formatting
- Implement data editing (cell, row, dialog, batch, template modes)
- Add sorting, filtering, and searching capabilities
- Handle row and cell operations (selection, templates, spanning)
- Optimize performance with virtual scrolling or infinite scrolling
- Configure paging and scrolling strategies
- Export data to PDF, Excel, or CSV formats
- Implement state persistence and aggregation
- Customize appearance with themes and styling
- Support accessibility and internationalization (RTL, localization)
TreeGrid Overview
The TreeGrid is optimized for displaying self-referential hierarchical data with:
- Auto-expand/collapse functionality for collapsible rows
- Enterprise features: virtual scrolling, aggregates, state persistence
- Adaptive UI for mobile and small screens
- Comprehensive export: PDF, Excel, CSV formats
- Full accessibility: WCAG compliance, keyboard navigation, ARIA support
- Internationalization: RTL support, locale customization
Data Structure Rules
Rule 1: childMapping is MANDATORY for Hierarchical Data
Severity: 🔴 CRITICAL - Grid will not expand/collapse without this
Requirement:
// ✅ REQUIRED - Must match data property name exactly
<TreeGridComponent
dataSource={data}
childMapping='subtasks'>
<ColumnsDirective>
{/* Columns */}
</ColumnsDirective>
</TreeGridComponent>
// ❌ WRONG - Will not work
<TreeGridComponent
dataSource={data}>
{/* No childMapping = No expansion possible */}
</TreeGridComponent>Data Format:
// ✅ CORRECT - childMapping matches 'subtasks' property
const data = [
{
TaskID: 1,
TaskName: 'Parent',
subtasks: [ // Must match childMapping value exactly
{ TaskID: 2, TaskName: 'Child' }
]
}
];Exception: Use idMapping + parentIdMapping for flat parent-child structure:
// Alternative: Flat structure with parent IDs
<TreeGridComponent
dataSource={flatData}
idMapping='TaskID'
parentIdMapping='ParentID'
hasChildMapping='isParent'>
</TreeGridComponent>---
Rule 2: Data Type Matching is MANDATORY
Severity: 🟠 IMPORTANT - Type mismatches cause rendering/sorting issues
Requirement:
// ✅ CORRECT - Type matches column definition
const data = [
{
TaskID: 1, // number type
TaskName: 'Planning', // string type
StartDate: new Date(), // Date object for date columns
}
];
// Column definition must match data types
<ColumnsDirective>
<ColumnDirective field='TaskID' headerText='ID' type='number'></ColumnDirective>
<ColumnDirective field='TaskName' headerText='Task' type='string'></ColumnDirective>
<ColumnDirective field='StartDate' headerText='Date' type='date' format='yMd'></ColumnDirective>
</ColumnsDirective>
// ❌ WRONG - Type mismatch
const data = [
{
TaskID: '1', // String instead of number
StartDate: '02/03/2024' // String instead of Date object
}
];Complete Props Reference
📄 Property and Methods: references/programmatic-api.md
📄 Events Reference 📖 references/events-reference.md
- Complete event reference guide
- Data events (actionBegin, actionComplete, actionFailure)
- Editing events (cellEdit, cellSave, beforeEdit)
- Selection events (rowSelected, rowDeselected, cellSelected)
- Expand/collapse events
- Column events (drag, drop, resize, hide, show)
- Row events and utilities
---
Documentation Navigation Guide
Data Binding
📖 references/data-binding.md
- Local vs. remote data sources
- Self-referential parent-child relationships
- DataManager integration
- ExpandStateMapping for initial expand state
- Complex data binding for nested objects
Column Configuration
📖 references/column.md
- Column definitions and field mapping
- Tree column index setup
- Data types and formatting
- Column templates and custom rendering
- Headers and styling
- Foreign key columns
Row Operations
📖 references/row.md
- Row templates and custom rendering
- Row height configuration
- Row spanning
- Detail templates for nested content
- Row drag-and-drop
- Indent and outdent operations
Cell Operations
📖 references/cell.md
- Cell editing
- Cell templates, styling and attributes
- Cell selection
- Cell formatting
Editing
📖 references/editing.md
- Cell editing mode
- Row editing mode
- Dialog editing
- Batch editing
- Template editing
- Validation rules and custom validators
- Command column editing
- Server-side persistence
Sorting
📖 references/sorting.md
- Single and multi-column sorting
- Initial sort order configuration
- Custom sort comparers
- Sorting with templates
- Programmatic sorting
Filtering
📖 references/filtering.md
- Filter bar mode
- Filter menu mode
- Excel-like filtering
- Custom filters
- Programmatic filtering
- Filter templates
Searching
📖 references/searching.md
- Global search across all columns
- Search highlight
- Programmatic search
- Search with templates
Selection
📖 references/selection.md
- Row selection (single and multiple)
- Cell selection
- Checkbox selection
- Selection mode configuration
- Programmatic selection
- Selection change events
Display & Layout (Performance & Presentation)
📄 Paging 📖 references/paging.md
- Pager configuration and options
- Page size settings
- Initial page settings
- Pager template customization
- Programmatic pagination
📄 Scrolling 📖 references/scrolling.md
- Vertical and horizontal scrolling
- Scroll height configuration
- Scroll position control
- Sticky headers
- Responsive scrolling
📄 Frozen Rows and Columns 📖 references/frozen-rows-columns.md
- Freeze header rows
- Freeze columns
- Freeze direction
- Lock columns
- IsFrozen property usage
📄 Virtual Scrolling & Infinite Scrolling 📖 references/virtual-scrolling-infinite-scrolling.md
- Virtual scrolling for large datasets
- Infinite scrolling configuration
- Row height for virtual scroll
- ViewportIndex and ViewportStartIndex
- Performance optimization with virtual scroll
Aggregates
📖 references/aggregates.md
- Standard aggregates (Sum, Average, Min, Max, Count)
- Custom aggregates
- Footer aggregates
- Group aggregates
- Hierarchical aggregates for child data
State Persistence
📖 references/state-persistence.md
- Global state persistence
- Local state persistence
- ExpandState tracking
- Persist sort, filter, and paging
- State restoration on reload
Toolbar
📖 references/toolbar.md
- Built-in toolbar items
- Custom toolbar items
- Toolbar item types (Button, Separator, Dropdown)
- Toolbar item click handling
- Toolbar template customization
Context Menu
📖 references/context-menu.md
- Built-in context menu items
- Custom context menu items
- Menu item click handling
- Copy/Edit/Delete operations
- Header context menu
Adaptive View
📖 references/adaptive-view.md
- Adaptive UI for small screens
- Full-screen dialogs
- Horizontal row rendering
- Responsive column adjustments
- Mobile optimization
Loading Animation
📖 references/loading-animation.md
- Loading indicator display
- Custom loading spinner
- Loading state management
- Spinner animation control
Export & Print
📄 PDF Export 📖 references/pdf-export.md
- PDF export configuration
- Export options (columns, format)
- Headers and footers
- Page orientation and size
- Server-side export
- Exporting hierarchical data
📄 Excel Export 📖 references/excel-export.md
- Excel export configuration
- Export options (columns, formatting)
- Cell styling in export
- Headers and footers
- Server-side export
- Exporting hierarchical levels
📄 Print 📖 references/print.md
- Print configuration
- Print with custom layout
- Print specific ranges
- Print hierarchy levels
- Print preview
Clipboard
📖 references/clipboard.md
- Copy cell content to clipboard
- Copy entire rows
- Paste operations
- Copy hierarchy with levels
- Clipboard event handling
Column Features (Advanced)
📄 Column Reorder 📖 references/column-reorder.md
- Enable column drag-and-drop reordering
- Prevent specific columns from reordering
- Reorder events and callbacks
- Programmatic column reordering
📄 Column Resize 📖 references/column-resize.md
- Enable column drag resize
- Auto-fit columns to content
- Column width constraints (min/max)
- Resize events
- Programmatic width changes
- Save/restore column widths
📄 Column Menu 📖 references/column-menu.md
- Built-in column menu items
- Sort, filter, and column chooser from menu
- Custom menu items
- Menu events and handlers
- Conditional menu items
📄 Column Chooser 📖 references/column-chooser.md
- Toggle column visibility
- Column chooser dialog configuration
- Show/hide columns programmatically
- Column visibility events
- Save/restore column visibility
📄 Command Column 📖 references/command-column.md
- Built-in command buttons (Edit, Delete, Save, Cancel)
- Custom command buttons
- Command click handlers
- Icon customization
- Row action patterns
Server Integration
📖 references/server-integration.md
- Remote data binding with DataManager
- Server-side CRUD operations
- Server-side filtering and sorting
- Server-side paging
- Custom request/response handling
- Error handling strategies
- Batch operations
Validation Patterns
📖 references/validation-patterns.md
- Column validation rules
- Custom validation logic
- Cross-field validation
- Async validation (API calls)
- Server-side validation
- Dialog form validation
- Conditional validation
- Error message display
Performance Optimization
📖 references/performance-optimization.md
- Virtual scrolling for large datasets (100k+ rows)
- Infinite scrolling with caching
- Bundle size optimization
- Disabling unnecessary features
- Server-side operations optimization
- Memoization and rendering optimization
- Template optimization
- Event handler optimization
- Benchmarking and performance monitoring
- Performance checklist
Customization
📖 references/styling-appearance.md
- CSS class customization
- Theme selection (Material, Bootstrap, Fluent, Tailwind)
- CSS variable overrides
- Component-specific styling
- Dark mode support
- Row styling
- Cell styling
Globalization
📖 references/globalization.md
- Internationalization (i18n)
- Localization (l10n) for UI text
- Right-to-left (RTL) support
- Date formatting per locale
- Number formatting per locale
- Currency localization
Accessibility
📖 references/accessibility.md
- WCAG 2.1 Level AA compliance
- Keyboard navigation (all shortcuts)
- ARIA attributes and screen reader support
- Expand/collapse keyboard shortcuts
- Editing keyboard navigation
- Selection shortcuts
Quick Start Example
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, Page } from '@syncfusion/ej2-react-treegrid';
import { DataManager, UrlAdaptor } from '@syncfusion/ej2-data';
interface ITreeData {
TaskID?: number;
TaskName?: string;
parentID: number | null;
Children?: ITreeData[];
}
export default function App() {
const data: ITreeData[] = [
{
TaskID: 1,
TaskName: 'Planning',
parentID: null,
Children: [{ TaskID: 2, TaskName: 'Plan timeline', parentID: 1 }]
}
];
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
treeColumnIndex={1}
height="auto"
allowPaging={true}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
<Inject services={[Page]} />
</TreeGridComponent>
);
}Core Configuration
| Property | Type | Default | Description |
|---|---|---|---|
dataSource | Array \ | DataManager | [] |
childMapping | string | null | Property name for child records (e.g., "Children") |
idMapping | string | null | Property name for unique ID (flat data) |
parentIdMapping | string | null | Property name for parent ID (flat data) |
hasChildMapping | string | null | Property for lazy load indicator |
treeColumnIndex | number | 0 | Column index for tree expand icons |
expandStateMapping | string | null | Property for initial expand state |
Accessibility
Table of Contents
The Syncfusion React TreeGrid is built with WCAG 2.1 Level AA accessibility standards and provides comprehensive keyboard navigation, ARIA support, and screen reader compatibility.
WCAG 2.1 Compliance
TreeGrid adheres to Web Content Accessibility Guidelines (WCAG) 2.1 Level AA, ensuring:
- Perceivable: Content is visible and distinguishable
- Operable: Fully keyboard navigable
- Understandable: Clear labels and predictable behavior
- Robust: Compatible with assistive technologies
Keyboard Navigation
TreeGrid provides complete keyboard accessibility without requiring mouse interaction.
Navigation Keys
Arrow Keys - Navigation
→ Left/Right: Move between columns (cell selection)
↑ Down: Move between rows
Ctrl + Home: Go to first cell
Ctrl + End: Go to last cellPage Navigation
Page Up: Move up by visible rows
Page Down: Move down by visible rows
Tab: Move to next focusable element
Shift + Tab: Move to previous focusable elementExpand/Collapse
Right (→): Expand parent row (when focused on tree column)
Left (←): Collapse parent row (when focused on tree column)
Space: Toggle expand/collapse for tree rowEditing Shortcuts
Cell Editing
F2 or Double-click: Start cell editing
Enter: Save and move to next row
Tab: Save and move to next cell
Shift + Tab: Save and move to previous cell
Escape: Cancel editingRow Editing
F2: Start row editing
Enter: Save row
Escape: Cancel editingBatch Editing
Tab: Move to next cell and start editing
Shift + Tab: Move to previous cell
Escape: Cancel current editSelection Shortcuts
Row Selection
Space: Select/deselect current row
Ctrl + Space: Toggle selection for current row
Ctrl + A: Select all rows
Ctrl + Home then Ctrl + Shift + End: Select from first to lastCell Selection
Space: Select/deselect current cell
Ctrl + C: Copy selected cell content
Ctrl + V: Paste content (if editing enabled)TreeGrid-Specific Keys
Hierarchy Navigation
When focus on tree column with expand/collapse icon:
→ Right: Expand children
← Left: Collapse children
↓ Down: Move to first child (if expanded)
↑ Up: Move to parent rowFiltering & Searching
Filter Interaction
Ctrl + F: Open search box
Enter: Execute search
Escape: Close searchFilter Menu
Alt + Down: Open filter menu
Arrow keys: Navigate filter options
Enter: Apply filter
Escape: Close menuARIA Attributes
TreeGrid automatically includes proper ARIA markup for screen readers:
Grid Structure
<div role="grid" aria-label="Data Table">
<div role="row" aria-level="1">
<div role="gridcell">Cell Content</div>
</div>
</div>Automatic ARIA for TreeGrid:
role="grid"- Identifies the component as a data gridrole="row"- Each row in the gridrole="gridcell"- Each cell in a rowrole="columnheader"- Column header cellsaria-rowindex- Current row positionaria-colindex- Current column positionaria-colcount- Total number of columnsaria-rowcount- Total number of rowsaria-expanded- For tree rows (true/false/undefined)aria-level- Hierarchy level in treearia-selected- For selected rows/cellsaria-label- Column headers and buttonsaria-describedby- Links cells to column descriptions
Expanded/Collapsed State
TreeGrid automatically updates ARIA attributes for expand/collapse:
// Parent row when expanded
<div role="row" aria-expanded="true" aria-level="1">
// Parent row when collapsed
<div role="row" aria-expanded="false" aria-level="1">
// Child rows (automatically marked as children)
<div role="row" aria-level="2" />
<div role="row" aria-level="3" />Selection ARIA
// Row selection
<div role="row" aria-selected="true">
// Cell selection
<div role="gridcell" aria-selected="true">Edit Mode ARIA
During cell/row editing:
<input aria-label="Task Name" aria-required="true" />Screen Reader Support
TreeGrid works with popular screen readers:
- NVDA - Full support
- JAWS - Full support
- VoiceOver - Full support
Screen Reader Announcements
Grid Navigation
- "Row 1, Column Name, contains Task ID"
- "Expanded, Level 1"
- "1 of 100 rows"
Row Operations
- "Expanding row"
- "Collapsing row"
- "Row selected"
- "3 rows selected"
Editing
- "Edit mode, Press Enter to save or Escape to cancel"
- "Cell validation failed"
Globalization & Localization
RTL Support - Full right-to-left language support:
<TreeGridComponent enableRtl={true}>
{/* Grid rendered RTL */}
</TreeGridComponent>Localization - Translated UI strings for:
- Proper text direction for each language
- Culturally appropriate formatting
import { L10n } from '@syncfusion/ej2-base';
L10n.load({
'es': {
'treegrid': {
'Add': 'Añadir',
'Edit': 'Editar'
}
}
});Adaptive View
Table of Contents
Enable Adaptive UI
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, Edit, Toolbar, Filter } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{ TaskID: 1, TaskName: 'Planning', Priority: 'High', Children: [] }
];
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
enableAdaptiveUI={true}
editSettings={{
mode: 'Dialog',
allowEditing: true,
allowAdding: true,
allowDeleting: true,
}}
toolbar={['Add', 'Edit', 'Delete']}
allowFiltering={true}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
<Inject services={[Edit, Toolbar, Filter]} />
</TreeGridComponent>
);
}Key APIs
| Property | Type | Description |
|---|---|---|
enableAdaptiveUI | boolean | Enable adaptive UI for small screens |
Common Patterns
1. Mobile-First: Design for mobile, enhance for desktop 2. Touch Events: Handle touch interactions 3. Responsive Dialogs: Full-screen edit forms on mobile 4. Flexible Layout: Adjust layout per screen size
Aggregates
Table of Contents
Overview
Display aggregate calculations (sum, average, min, max, count) at multiple levels in TreeGrid.
Standard Aggregates
Built-in aggregate functions:
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, Aggregate, AggregatesDirective, AggregateDirective, AggregateColumnsDirective, AggregateColumnDirective } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{
TaskID: 1,
TaskName: 'Planning',
Budget: 10000,
Children: [
{ TaskID: 2, TaskName: 'Plan timeline', Budget: 2000 },
{ TaskID: 3, TaskName: 'Plan budget', Budget: 3000 }
]
}
];
return (
<TreeGridComponent dataSource={data} childMapping="Children" treeColumnIndex={1}>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Budget" headerText="Budget" width={120} type="number" format="N2" />
</ColumnsDirective>
<AggregatesDirective>
<AggregateDirective>
<AggregateColumnsDirective>
<AggregateColumnDirective field="Budget" type="Sum" footerTemplate="Total: ${Sum}" />
<AggregateColumnDirective field="Budget" type="Average" footerTemplate="Avg: ${Average}" />
</AggregatesDirective>
</AggregateDirective>
</AggregatesDirective>
<Inject services={[Aggregate]} />
</TreeGridComponent>
);
}Custom Aggregates
Implement custom calculation logic:
<AggregatesDirective>
<AggregateDirective>
<AggregateColumnsDirective>
<AggregateColumnDirective
field="Budget"
type="Custom"
customAggregate={(data) => {
return data.reduce((sum, item) => sum + item.Budget, 0) / data.length;
}}
footerTemplate="Weighted Avg: ${Custom}"
/>
</AggregateColumnsDirective>
</AggregateDirective>
</AggregatesDirective>Footer Aggregates
Display aggregates in footer rows:
<AggregatesDirective>
<AggregateDirective>
<AggregateColumnsDirective>
<AggregateColumnDirective field="Budget" type="Sum" footerTemplate="Total Budget: ${Sum}" />
<AggregateColumnDirective field="TaskID" type="Count" footerTemplate="Items: ${Count}" />
</AggregateColumnsDirective>
</AggregateDirective>
</AggregatesDirective>Group Aggregates
Show aggregates at each hierarchy level:
<AggregatesDirective>
<AggregateDirective>
<AggregateColumnsDirective>
<AggregateColumnDirective
field="Budget"
type="Sum"
footerTemplate="Subtotal: ${Sum}"
showChildSummary={true}
/>
</AggregateColumnsDirective>
</AggregateDirective>
</AggregatesDirective>Key APIs
| Property | Type | Description |
|---|---|---|
type | string | 'Sum', 'Average', 'Min', 'Max', 'Count', 'Custom' |
field | string | Column to aggregate |
footerTemplate | string | Display format in footer |
customAggregate | function | Custom aggregate logic |
showChildSummary | boolean | Show per-level aggregates |
Common Patterns
1. Financial Summary: Sum totals at each level 2. Data Statistics: Count, average, min/max for metrics 3. Hierarchical Summary: Aggregate child data with parent totals
Cell
Table of Contents
Cell Editing
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, Edit, Toolbar } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{ TaskID: 1, TaskName: 'Planning', Priority: 'High', Children: [] }
];
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
editSettings={{ mode: 'Cell', allowEditing: true }}
toolbar={['Edit', 'Update', 'Cancel']}
treeColumnIndex={1}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} allowEditing={false} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Priority" headerText="Priority" width={100} editType="dropdownedit" />
</ColumnsDirective>
<Inject services={[Edit, Toolbar]} />
</TreeGridComponent>
);
}Cell Templates
Use templates for custom cell rendering:
const statusCell = (props) => {
const statusColor = props.Priority === 'High' ? 'red' : 'green';
return <span style={{ color: statusColor }}>{props.Priority}</span>;
};
<ColumnDirective
field="Priority"
headerText="Priority"
template={statusCell}
width={100}
/>Cell Styling
Apply custom styling to cells:
<TreeGridComponent
dataSource={data}
childMapping="Children"
queryCellInfo={(args) => {
if (args.column.field === 'Priority' && args.data.Priority === 'High') {
args.cell.style.backgroundColor = 'red';
args.cell.style.color = 'white';
}
}}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
</TreeGridComponent>Cell Selection
Enable cell-based selection:
<TreeGridComponent
dataSource={data}
childMapping="Children"
selectionSettings={{ type: 'Cell', mode: 'Box' }}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
</TreeGridComponent>Selection Modes:
Single: Select one cellMultiple: Select multiple cells with Ctrl+ClickBox: Select cell range
Cell Formatting
Format cell values:
<ColumnsDirective>
<ColumnDirective field="Budget" headerText="Budget" type="number" format="N2" width={120} />
<ColumnDirective field="StartDate" headerText="Start Date" type="date" format="yMd" width={120} />
</ColumnsDirective>Key APIs
| Property/Event | Type | Description |
|---|---|---|
template | JSX | Custom cell render template |
editType | string | Edit type: 'textbox', 'dropdownedit', 'datepickeredit', etc. |
allowEditing | boolean | Enable/disable cell editing for column |
queryCellInfo | event | Customize cell styling |
cellDeselecting | event | Fired before cell deselection |
cellSelected | event | Fired when cell is selected |
Common Patterns
1. Conditional Formatting: Use queryCellInfo to apply styles based on values 2. Custom Editors: Implement specific edit types for data input 3. Cell Tooltips: Add tooltips for cell content 4. Cell Navigation: Implement keyboard navigation between cells
Clipboard
Table of Contents
Copy Operations
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{ TaskID: 1, TaskName: 'Planning', Priority: 'High', Children: [] }
];
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
selectionSettings={{ type: 'Cell', mode: 'Multiple' }}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
<Inject services={[]} />
</TreeGridComponent>
);
}
// Copy with Ctrl+C, Paste with Ctrl+VCopy with Hierarchy
Copy with hierarchical structure preserved:
import { DropDownListComponent } from "@syncfusion/ej2-react-dropdowns";
import { ColumnDirective, ColumnsDirective } from '@syncfusion/ej2-react-treegrid';
import { TreeGridComponent } from '@syncfusion/ej2-react-treegrid';
import * as React from 'react';
import { sampleData } from './datasource';
function App() {
let treegrid;
const data = ['Parent', 'Child', 'Both', 'None'];
const settings = { type: 'Multiple', mode: 'Row' };
const onChange = (sel) => {
const mode = sel.value.toString();
if (treegrid) {
treegrid.copyHierarchyMode = mode;
}
};
return (<div>
<DropDownListComponent popupHeight="250px" dataSource={data} value="Parent" change={onChange} width="150px"/>
<TreeGridComponent dataSource={sampleData} treeColumnIndex={1} childMapping='subtasks' height='225' allowFiltering={true} selectionSettings={settings} ref={g => treegrid = g}>
<ColumnsDirective>
<ColumnDirective field='taskID' headerText='Task ID' width='75' 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>
</TreeGridComponent>
</div>);
}
;
export default App;Key APIs
| Property/Method | Type | Description |
|---|---|---|
copy | method | Copy to clipboard |
Common Patterns
1. Bulk Copy: Copy multiple rows to paste into Excel 2. Copy with Format: Preserve hierarchy indentation 3. CSV Export: Format and copy as CSV
Column Chooser
Table of Contents
- Enable Column Chooser
- Column Chooser Dialog
- Show/Hide Columns
- Visibility Events
- Show/Hide Columns Programmatically
- Key APIs
- Common Patterns
Enable Column Chooser
Enable users to toggle column visibility:
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, ColumnChooser, Toolbar } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{ TaskID: 1, TaskName: 'Planning', StartDate: new Date(2018, 2, 3), EndDate: new Date(2018, 2, 7), Priority: 'High', Children: [] }
];
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
toolbar={['ColumnChooser']}
showColumnChooser={true}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="StartDate" headerText="Start Date" width={120} type="date" format="yMd" />
<ColumnDirective field="EndDate" headerText="End Date" width={120} type="date" format="yMd" />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
<Inject services={[ColumnChooser, Toolbar]} />
</TreeGridComponent>
);
}Column Chooser Dialog
Configure column chooser settings:
<TreeGridComponent
dataSource={data}
childMapping="Children"
showColumnChooser={true}
columnChooserSettings={{
title: 'Column Visibility',
columnsItemsCount: 5, // Items per scroll
search: true, // Search box in dialog
hideColumns: [] // Columns hidden by default
}}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} showInColumnChooser={true} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} showInColumnChooser={true} />
<ColumnDirective field="Priority" headerText="Priority" width={100} showInColumnChooser={true} />
</ColumnsDirective>
<Inject services={[ColumnChooser]} />
</TreeGridComponent>Show/Hide Columns
Hide columns from column chooser:
<ColumnsDirective>
<ColumnDirective
field="TaskID"
headerText="Task ID"
width={80}
showInColumnChooser={false} // Not shown in chooser
/>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} showInColumnChooser={true} />
</ColumnsDirective>Show/Hide Columns Programmatically
Control column visibility via code:
const treeGridRef = React.useRef();
const hideColumn = (fieldName) => {
const column = treeGridRef.current.columns.find(col => col.field === fieldName);
if (column) {
column.visible = false;
treeGridRef.current.refreshColumns();
}
};
const showColumn = (fieldName) => {
const column = treeGridRef.current.columns.find(col => col.field === fieldName);
if (column) {
column.visible = true;
treeGridRef.current.refreshColumns();
}
};
const toggleColumn = (fieldName) => {
const column = treeGridRef.current.columns.find(col => col.field === fieldName);
if (column) {
column.visible = !column.visible;
treeGridRef.current.refreshColumns();
}
};
<TreeGridComponent
ref={treeGridRef}
dataSource={data}
childMapping="Children"
showColumnChooser={true}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
<Inject services={[ColumnChooser]} />
</TreeGridComponent>Visibility Events
Listen to column visibility changes:
<TreeGridComponent
dataSource={data}
childMapping="Children"
showColumnChooser={true}
columnHide={(args) => {
console.log('Column hidden:', args.column.field);
}}
columnShow={(args) => {
console.log('Column shown:', args.column.field);
}}
>
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
<Inject services={[ColumnChooser]} />
</TreeGridComponent>Key APIs
| Property/Event | Type | Description |
|---|---|---|
showColumnChooser | boolean | Enable column chooser |
columnChooserSettings | object | Configure chooser dialog |
showInColumnChooser | boolean | Show column in chooser dialog |
visible | boolean | Column visibility |
columnHide | event | Fired when column is hidden |
columnShow | event | Fired when column is shown |
Common Patterns
1. User Preferences: Save visible columns per user 2. Report Builder: Let users select columns to display 3. Mobile Optimization: Hide columns on small screens 4. Role-based Visibility: Show different columns per role
Column Menu
Table of Contents
- Enable Column Menu
- Menu Items
- Disable Column Menu for Specific Columns
- Custom Menu Items
- Menu Events
- Key APIs
- Common Patterns
Enable Column Menu
Enable column header dropdown menu:
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, ColumnMenu, Sort, Filter } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{ TaskID: 1, TaskName: 'Planning', Priority: 'High', Children: [] }
];
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
showColumnMenu={true}
allowSorting={true}
allowFiltering={true}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} showColumnMenu={true} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} showColumnMenu={true} />
<ColumnDirective field="Priority" headerText="Priority" width={100} showColumnMenu={true} />
</ColumnsDirective>
<Inject services={[ColumnMenu, Sort, Filter]} />
</TreeGridComponent>
);
}Menu Items
Default menu items available:
<TreeGridComponent
dataSource={data}
childMapping="Children"
showColumnMenu={true}
>
{/* Default menu includes:
- Sort Ascending
- Sort Descending
- Clear Sorting
- Filter
- Column Chooser
- Auto Fit
*/}
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
<Inject services={[ColumnMenu]} />
</TreeGridComponent>Disable Column Menu for Specific Columns
<ColumnsDirective>
<ColumnDirective
field="TaskID"
headerText="Task ID"
width={80}
showColumnMenu={false} // No menu for this column
/>
<ColumnDirective
field="TaskName"
headerText="Task Name"
width={160}
showColumnMenu={true} // Menu enabled
/>
</ColumnsDirective>Custom Menu Items
Add custom items to column menu:
<TreeGridComponent
dataSource={data}
childMapping="Children"
showColumnMenu={true}
columnMenuItems={[
'SortAscending',
'SortDescending',
'SortByCellValue',
'Filter',
'Separator',
'ColumnChooser',
'Separator',
{ text: 'Copy to Clipboard', id: 'copyToClipboard' },
{ text: 'Export Column', id: 'exportColumn' }
]}
columnMenuItemClick={(args) => {
if (args.item.id === 'copyToClipboard') {
console.log('Copying column:', args.column.field);
// Implement copy logic
}
if (args.item.id === 'exportColumn') {
console.log('Exporting column:', args.column.field);
// Implement export logic
}
}}
>
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
<Inject services={[ColumnMenu]} />
</TreeGridComponent>Menu Events
Handle column menu interactions:
<TreeGridComponent
dataSource={data}
childMapping="Children"
showColumnMenu={true}
columnMenuOpen={(args) => {
console.log('Menu opened for column:', args.column.field);
}}
columnMenuItemClick={(args) => {
console.log('Menu item clicked:', args.item.text);
}}
>
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
<Inject services={[ColumnMenu]} />
</TreeGridComponent>Key APIs
| Property/Event | Type | Description |
|---|---|---|
showColumnMenu | boolean | Enable column menu |
showColumnMenu (column) | boolean | Enable menu for specific column |
columnMenuItems | array | Custom menu configuration |
columnMenuOpen | event | Fired when menu opens |
columnMenuItemClick | event | Fired when menu item is clicked |
Common Patterns
1. Quick Sort: Add sort options directly 2. Column Control: Hide/show columns from menu 3. Export Column: Export specific columns 4. Quick Actions: Custom operations per column
Column Reorder
Table of Contents
- Enable Column Reorder
- Reorder Configuration
- Prevent Specific Columns from Reordering
- Reorder Events
- Programmatic Column Reordering
- Key APIs
- Common Patterns
Enable Column Reorder
Enable users to reorder columns by dragging:
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, Reorder } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{ TaskID: 1, TaskName: 'Planning', StartDate: new Date(2018, 2, 3), Priority: 'High', Children: [] }
];
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
allowReordering={true}
treeColumnIndex={1}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="StartDate" headerText="Start Date" width={120} type="date" format="yMd" />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
<Inject services={[Reorder]} />
</TreeGridComponent>
);
}Reorder Configuration
Configure column reorder behavior:
<TreeGridComponent
dataSource={data}
childMapping="Children"
allowReordering={true}
reorderSettings={{
allowReordering: true,
allowKeyboard: true, // Keyboard navigation for reorder
}}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} allowReordering={false} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} allowReordering={true} />
</ColumnsDirective>
<Inject services={[Reorder]} />
</TreeGridComponent>Prevent Specific Columns from Reordering
Disable reorder for specific columns:
<ColumnsDirective>
<ColumnDirective
field="TaskID"
headerText="Task ID"
width={80}
allowReordering={false} // This column cannot be reordered
/>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>Reorder Events
Listen to reorder events:
<TreeGridComponent
dataSource={data}
childMapping="Children"
allowReordering={true}
columnDragStart={(args) => {
console.log('Dragging column:', args.column.field);
}}
columnDrag={(args) => {
console.log('Column being dragged over:', args.column);
}}
columnDrop={(args) => {
console.log('Column dropped');
console.log('From index:', args.fromColumn);
console.log('To index:', args.toColumn);
}}
>
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
<Inject services={[Reorder]} />
</TreeGridComponent>Programmatic Column Reordering
Reorder columns via code:
const treeGridRef = React.useRef();
const reorderColumns = () => {
// Move column at index 0 to position 2
treeGridRef.current.reorderColumns([1, 2, 0]);
};
<TreeGridComponent
ref={treeGridRef}
dataSource={data}
childMapping="Children"
allowReordering={true}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
<Inject services={[Reorder]} />
</TreeGridComponent>Key APIs
| Property/Event | Type | Description |
|---|---|---|
allowReordering | boolean | Enable column reordering |
reorderSettings | object | Reorder configuration |
allowReordering (column) | boolean | Allow reordering for specific column |
columnDragStart | event | Fired when column drag starts |
columnDrag | event | Fired while dragging column |
columnDrop | event | Fired when column is dropped |
reorderColumns | method | Programmatically reorder columns |
Common Patterns
1. User Preferences: Save reordered columns per user 2. Fixed First Column: Disable reordering for ID columns 3. Keyboard Navigation: Allow reordering via arrow keys 4. Visual Feedback: Show drop position during drag
Column Resize
Table of Contents
- Enable Column Resize
- Resize Configuration
- Disable Resize for Specific Columns
- Auto-fit Columns
- Column Width Constraints
- Resize Events
- Programmatic Column Width Change
Enable Column Resize
Enable users to resize columns by dragging:
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, Resize } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{ TaskID: 1, TaskName: 'Planning', Description: 'Project planning and preparation', Priority: 'High', Children: [] }
];
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
allowResizing={true}
treeColumnIndex={1}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Description" headerText="Description" width={200} />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
<Inject services={[Resize]} />
</TreeGridComponent>
);
}Resize Configuration
Configure resize behavior:
<TreeGridComponent
dataSource={data}
childMapping="Children"
allowResizing={true}
resizeSettings={{
mode: 'Normal' // 'Normal' or 'Auto'
}}
>
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} allowResizing={true} />
</ColumnsDirective>
<Inject services={[Resize]} />
</TreeGridComponent>Disable Resize for Specific Columns
Prevent resizing on individual columns:
<ColumnsDirective>
<ColumnDirective
field="TaskID"
headerText="Task ID"
width={80}
allowResizing={false} // This column cannot be resized
/>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} allowResizing={true} />
</ColumnsDirective>Auto-fit Columns
Automatically fit columns to content:
const treeGridRef = React.useRef();
const autoFitColumns = () => {
treeGridRef.current.autoFitColumns(); // All columns
};
const autoFitSpecificColumns = () => {
treeGridRef.current.autoFitColumns(['TaskName', 'Description']); // Specific columns
};
<TreeGridComponent ref={treeGridRef} dataSource={data} childMapping="Children" allowResizing={true}>
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Description" headerText="Description" width={200} />
</ColumnsDirective>
<Inject services={[Resize]} />
</TreeGridComponent>Column Width Constraints
Set minimum and maximum width:
<ColumnsDirective>
<ColumnDirective
field="TaskName"
headerText="Task Name"
width={160}
minWidth={100} // Minimum width on resize
maxWidth={300} // Maximum width on resize
/>
<ColumnDirective
field="Description"
headerText="Description"
width={200}
minWidth={150}
maxWidth={500}
/>
</ColumnsDirective>Resize Events
Listen to column resize events:
<TreeGridComponent
dataSource={data}
childMapping="Children"
allowResizing={true}
resizeStart={(args) => {
console.log('Resize started for column:', args.column.field);
console.log('Current width:', args.column.width);
}}
resizing={(args) => {
console.log('Resizing column:', args.column.field);
console.log('New width:', args.newWidth);
}}
resizeStop={(args) => {
console.log('Resize completed for column:', args.column.field);
console.log('Final width:', args.column.width);
// Save new width to database
}}
>
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
<Inject services={[Resize]} />
</TreeGridComponent>Programmatic Column Width Change
Set column width via code:
const setColumnWidth = (fieldName, width) => {
const column = treeGridRef.current.columns.find(col => col.field === fieldName);
if (column) {
column.width = width;
treeGridRef.current.refreshColumns();
}
};
// Usage:
setColumnWidth('TaskName', 250);Key APIs
| Property/Event | Type | Description |
|---|---|---|
allowResizing | boolean | Enable column resizing |
resizeSettings | object | Resize configuration |
allowResizing (column) | boolean | Allow resize for specific column |
minWidth | number/string | Minimum column width |
maxWidth | number/string | Maximum column width |
resizeStart | event | Fired when resize starts |
resizing | event | Fired while resizing |
resizeStop | event | Fired when resize completes |
autoFitColumns | method | Auto-fit to content |
Common Patterns
1. Responsive Resize: Adjust widths based on screen size 2. User Preferences: Save column widths per user 3. Min/Max Constraints: Ensure columns stay readable 4. Content-based Auto-fit: Resize to fit longest content 5. Fixed Column Width: Lock specific columns
Column
Table of Contents
- Overview
- Column Definitions
- Tree Column Index
- Column Formatting
- Column Templates
- Foreign Key Columns
- Column Headers
Overview
Columns define the schema for rendering TreeGrid data. Each column maps to a data source property and controls display format, editing behavior, and rendering.
Key Concepts:
- Column definitions via
ColumnDirectivecomponent fieldproperty maps to data source propertytreeColumnIndexdesignates the column with expand/collapse iconschildMappingdefines parent-child relationships
Column Definitions
Define columns using ColumnDirective component:
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{
TaskID: 1,
TaskName: 'Planning',
StartDate: new Date(2018, 2, 3),
EndDate: new Date(2018, 2, 7),
Priority: 'High',
Children: [
{ TaskID: 2, TaskName: 'Plan timeline', StartDate: new Date(2018, 2, 3), Priority: 'Normal' }
]
}
];
return (
<TreeGridComponent dataSource={data} childMapping="Children" treeColumnIndex={1}>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} type="number" />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="StartDate" headerText="Start Date" type="date" format="yMd" width={120} />
<ColumnDirective field="EndDate" headerText="End Date" type="date" format="yMd" width={120} />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
</TreeGridComponent>
);
}Tree Column Index
The treeColumnIndex property designates which column displays expand/collapse icons:
// Tree icons appear in TaskName column (index 1)
<TreeGridComponent dataSource={data} childMapping="Children" treeColumnIndex={1}>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} /> {/* Tree column here */}
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
</TreeGridComponent>Default: treeColumnIndex={0} (first column)
Column Formatting
Format column values using format and type properties:
<ColumnsDirective>
{/* Number formatting */}
<ColumnDirective field="Budget" headerText="Budget" type="number" format="N2" width={100} />
{/* Currency formatting */}
<ColumnDirective field="Price" headerText="Price" type="number" format="C2" width={100} />
{/* Percentage formatting */}
<ColumnDirective field="Progress" headerText="Progress" type="number" format="P0" width={100} />
{/* Date formatting */}
<ColumnDirective field="StartDate" headerText="Start Date" type="date" format="yMd" width={120} />
<ColumnDirective field="EndDate" headerText="End Date" type="date" format="dd/MM/yyyy" width={120} />
</ColumnsDirective>Format Strings:
- Number:
N(N2, N3, etc.) - Currency:
C(C2, C3, etc.) - Percentage:
P(P0, P2, etc.) - Date:
yMd,dd/MM/yyyy,MMM d, yyyy
Column Templates
Use templates for custom column rendering:
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, Page } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{ TaskID: 1, TaskName: 'Planning', Status: 'In Progress', Progress: 45, Children: [] }
];
const statusTemplate = (props) => {
const statusColor = props.Status === 'Completed' ? 'green' : props.Status === 'In Progress' ? 'orange' : 'red';
return (
<div style={{ backgroundColor: statusColor, color: 'white', padding: '5px' }}>
{props.Status}
</div>
);
};
const progressTemplate = (props) => {
return (
<div style={{ width: '100px', backgroundColor: '#f0f0f0', borderRadius: '3px' }}>
<div style={{ width: props.Progress + '%', backgroundColor: 'blue', height: '20px', textAlign: 'center' }}>
{props.Progress}%
</div>
</div>
);
};
return (
<TreeGridComponent dataSource={data} childMapping="Children" treeColumnIndex={1}>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Status" headerText="Status" width={100} template={statusTemplate} />
<ColumnDirective field="Progress" headerText="Progress" width={150} template={progressTemplate} />
</ColumnsDirective>
<Inject services={[Page]} />
</TreeGridComponent>
);
}Foreign Key Columns
Map columns to related data using foreign key references:
import * as React from 'react';
import { foreignKeyData } from './datasource';
import { dropData } from './datasource';
import { DataManager, Query } from '@syncfusion/ej2-data';
import { ColumnsDirective, ColumnDirective, TreeGridComponent, Inject } from '@syncfusion/ej2-react-treegrid';
import { Edit, Page, Toolbar } from '@syncfusion/ej2-react-treegrid';
/* tslint:disable */
function App() {
let treegrid;
const editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Cell' };
const toolbarOptions = ['Add', 'Delete', 'Update', 'Cancel'];
const queryCellInfo = (args) => {
if (args.column.field === "EmployeeID") {
for (var i = 0; i < dropData.length; i++) {
let data = args.data;
if (data[args.column.field] === dropData[i]["EmployeeID"]) {
args.cell.innerText = dropData[i]["EmployeeName"]; // assign the foreignkey field value to the innertext
}
}
}
};
// Bind ForeignKey DataSource for dropdown using editParams
const employeeParams = {
params: {
dataSource: new DataManager(dropData),
fields: { text: "EmployeeName", value: "EmployeeID" },
query: new Query()
}
};
return (<TreeGridComponent dataSource={foreignKeyData} treeColumnIndex={0} childMapping='Children' height={280} toolbar={toolbarOptions} editSettings={editSettings} ref={g => treegrid = g} queryCellInfo={queryCellInfo}>
<ColumnsDirective>
<ColumnDirective field='EmpID' headerText='EmpID' isPrimaryKey={true} width='70'></ColumnDirective>
<ColumnDirective field='Name' headerText='Employee Name' width='70' isPrimaryKey={true}></ColumnDirective>
<ColumnDirective field='Contact' headerText='Contact' width='90' textAlign='Right'/>
<ColumnDirective field='DOB' headerText='DOB' width='70' format='yMd' textAlign='Right' editType='datepickeredit'></ColumnDirective>
<ColumnDirective field='EmployeeID' headerText='Employee ID' width='70' editType='dropdownedit' edit={employeeParams}></ColumnDirective>
<ColumnDirective field='Country' headerText='Country' width='90' textAlign='Right'/>
</ColumnsDirective>
<Inject services={[Page, Edit, Toolbar]}/>
</TreeGridComponent>);
}
;
export default App;Column Headers
Customize column headers with custom classes and orientation:
<TreeGridComponent
dataSource={data}
childMapping="Children"
treeColumnIndex={1}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} headerTemplate={<span style={{ color: 'red' }}>Task ID</span>} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
</TreeGridComponent>Key APIs
| Property | Type | Description |
|---|---|---|
field | string | Maps to data source property (required) |
headerText | string | Column header display text |
type | string | Data type: 'date', 'number', 'boolean', 'string' |
format | string | Display format (N2, C2, yMd, etc.) |
width | number/string | Column width in pixels or percentage |
allowSorting | boolean | Enable/disable sorting (default: true) |
allowFiltering | boolean | Enable/disable filtering (default: true) |
template | JSX | Custom rendering for cells |
headerTemplate | JSX | Custom rendering for header |
treeColumnIndex | number | Column index for tree expand/collapse icons |
allowSearch | boolean | Include in search (default: true) |
Common Patterns
1. Read-only Display: Set allowEditing={false} 2. Hidden Columns: Set visible={false} 3. Custom Alignment: Use textAlign="center" or textAlign="right" 4. Dynamic Width: Use responsive width calculations 5. Multi-level Headers: Group columns with header template
Command Column
Table of Contents
- Built-in Commands
- Custom Commands Columns
- Handle Command Actions Events
- Icon Customization
- Command Column with Template
- Built-in Command Types
- Key APIs
- Common Patterns
Built-in Commands
Command column provides action buttons:
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, Edit, Toolbar, CommandColumn } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{ TaskID: 1, TaskName: 'Planning', Priority: 'High', Children: [] }
];
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
editSettings={{ allowEditing: true, allowDeleting: true, allowAdding: true }}
toolbar={['Add', 'Edit', 'Delete', 'Update', 'Cancel']}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} type="number" />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
<ColumnDirective
type="checkbox"
width={50}
/>
<ColumnDirective
type="CommandColumn"
headerText="Commands"
width={120}
commands={[
{ type: 'Edit', buttonOption: { cssClass: 'e-flat', iconCss: 'e-icons e-edit' } },
{ type: 'Delete', buttonOption: { cssClass: 'e-flat', iconCss: 'e-icons e-delete' } },
{ type: 'Save', buttonOption: { cssClass: 'e-flat', iconCss: 'e-icons e-save' } },
{ type: 'Cancel', buttonOption: { cssClass: 'e-flat', iconCss: 'e-icons e-cancel' } }
]}
/>
</ColumnsDirective>
<Inject services={[Edit, Toolbar, CommandColumn]} />
</TreeGridComponent>
);
}Custom Commands Columns
Add custom action buttons:
<ColumnDirective
type="CommandColumn"
headerText="Actions"
width={150}
commands={[
{ type: 'Edit', buttonOption: { cssClass: 'e-flat' } },
{
text: 'Copy',
buttonOption: { cssClass: 'e-flat' },
id: 'copy'
},
{
text: 'Details',
buttonOption: { cssClass: 'e-flat' },
id: 'details'
}
]}
/>Handle Command Actions Events
Get command click events:
<TreeGridComponent
dataSource={data}
childMapping="Children"
commandClick={(args) => {
console.log('Command:', args.commandColumn.type);
console.log('Row Data:', args.rowData);
if (args.commandColumn.type === 'Edit') {
// Handle edit
} else if (args.commandColumn.id === 'copy') {
// Handle custom copy command
const rowData = args.rowData;
console.log('Copying row:', rowData);
} else if (args.commandColumn.id === 'details') {
// Show details dialog
console.log('Showing details for:', args.rowData.TaskID);
}
}}
>
<ColumnsDirective>
<ColumnDirective
type="CommandColumn"
headerText="Actions"
width={150}
commands={[
{ type: 'Edit' },
{ text: 'Copy', id: 'copy' },
{ text: 'Details', id: 'details' }
]}
/>
</ColumnsDirective>
<Inject services={[Edit, CommandColumn]} />
</TreeGridComponent>Icon Customization
Customize command button icons:
<ColumnDirective
type="CommandColumn"
headerText="Commands"
width={150}
commands={[
{
type: 'Edit',
buttonOption: {
cssClass: 'e-outline',
iconCss: 'e-icons e-edit',
tooltip: 'Edit this row'
}
},
{
type: 'Delete',
buttonOption: {
cssClass: 'e-danger e-outline',
iconCss: 'e-icons e-delete',
tooltip: 'Delete this row'
}
},
{
text: 'Export',
buttonOption: {
cssClass: 'e-info e-outline',
iconCss: 'e-icons e-export-excel',
tooltip: 'Export row'
}
}
]}
/>Command Column with Template
Custom action templates:
const commandTemplate = (props) => {
return (
<div>
<button
onClick={() => handleEdit(props)}
style={{ marginRight: '5px' }}
>
Edit
</button>
<button
onClick={() => handleDelete(props)}
style={{ marginRight: '5px' }}
>
Delete
</button>
<button onClick={() => handleView(props)}>
View
</button>
</div>
);
};
const handleEdit = (rowData) => console.log('Edit:', rowData);
const handleDelete = (rowData) => console.log('Delete:', rowData);
const handleView = (rowData) => console.log('View:', rowData);
<ColumnDirective
headerText="Commands"
width={200}
template={commandTemplate}
/>Built-in Command Types
Available command types:
// Standard commands:
{ type: 'Edit' } // Edit the row
{ type: 'Delete' } // Delete the row
{ type: 'Save' } // Save edited row
{ type: 'Cancel' } // Cancel editing
{ type: 'Add' } // Add new row
// Custom command:
{ text: 'Custom', id: 'custom' }Key APIs
| Property/Event | Type | Description |
|---|---|---|
type | string | Column type: 'CommandColumn' |
commands | array | Array of command objects |
commandClick | event | Fired when command is clicked |
type (command) | string | 'Edit', 'Delete', 'Save', 'Cancel', 'Add' |
buttonOption | object | Button styling and icons |
Common Patterns
1. Row Actions: Edit, Delete, View buttons 2. Bulk Operations: Select multiple rows with checkboxes 3. Custom Workflows: Status change, Approve, Reject buttons 4. Context Actions: Actions based on row status
Context Menu
Table of Contents
- Built-in Context Menu
- Custom Context Menu Items
- Header Context Menu
- Context Menu Events
- Key APIs
- Common Patterns
Built-in Context Menu
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, ContextMenu, Edit } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{ TaskID: 1, TaskName: 'Planning', Priority: 'High', Children: [] }
];
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
contextMenuItems={['Copy', 'Edit', 'Delete']}
editSettings={{ allowEditing: true, allowDeleting: true }}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
<Inject services={[ContextMenu, Edit]} />
</TreeGridComponent>
);
}Custom Context Menu Items
Add custom menu items:
const contextMenuItems = [
{ text: 'Edit', target: '.e-grid', command: 'Edit' },
{ text: 'Delete', target: '.e-grid', command: 'Delete' },
{ type: 'Separator' },
{ text: 'Export', target: '.e-grid', id: 'export' },
{ text: 'Refresh', target: '.e-grid', id: 'refresh' }
];
<TreeGridComponent
dataSource={data}
childMapping="Children"
contextMenuItems={contextMenuItems}
contextMenuClick={(args) => {
if (args.item.id === 'export') {
console.log('Export clicked');
}
if (args.item.id === 'refresh') {
console.log('Refresh clicked');
}
}}
>
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
<Inject services={[ContextMenu, Edit]} />
</TreeGridComponent>Header Context Menu
Context menu for column headers:
const headerContextMenuItems = [
{ text: 'Sort Ascending', target: '.e-headercell', command: 'SortAscending' },
{ text: 'Sort Descending', target: '.e-headercell', command: 'SortDescending' },
{ type: 'Separator' },
{ text: 'Filter', target: '.e-headercell', command: 'Filter' }
];
<TreeGridComponent
dataSource={data}
childMapping="Children"
contextMenuItems={headerContextMenuItems}
>
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
<Inject services={[ContextMenu]} />
</TreeGridComponent>Context Menu Events
Handle menu item selection:
<TreeGridComponent
dataSource={data}
childMapping="Children"
contextMenuItems={['Copy', 'Edit', 'Delete']}
contextMenuClick={(args) => {
console.log('Menu item clicked:', args.item);
console.log('Row data:', args.rowInfo);
}}
contextMenuOpen={(args) => {
console.log('Context menu opened at:', args.currentTarget);
}}
>
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
<Inject services={[ContextMenu, Edit]} />
</TreeGridComponent>Key APIs
| Property/Event | Type | Description |
|---|---|---|
contextMenuItems | array | Menu items configuration |
contextMenuClick | event | Fired when menu item is clicked |
contextMenuOpen | event | Fired when menu opens |
text | string | Menu item label |
command | string | Built-in command (Edit, Delete, etc.) |
target | string | Target selector for menu |
Common Patterns
1. CRUD Operations: Edit, add, delete from context menu 2. Export Actions: Export selected rows 3. Advanced Actions: Custom business logic 4. Conditional Menu: Show items based on row data
Data Binding
Table of Contents
- Data Binding Approaches
- Hierarchical Data with childMapping
- Flat Data with parentID
- Remote Data Binding
- ExpandStateMapping
- Dynamic Data Loading
- Complex Data Binding
- Key APIs
- Common Patterns
Data Binding Approaches
TreeGrid supports multiple data binding approaches for hierarchical data:
// Node represents relationship type
Local Array → childMapping (nested Children property)
Flat Array → parentID (foreign key mapping)
Remote Service → DataManager with UrlAdaptorBasic Setup
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, Page } from '@syncfusion/ej2-react-treegrid';
interface ITreeData {
TaskID: number;
TaskName: string;
Duration?: number;
Children?: ITreeData[];
}
export default function App() {
const data: ITreeData[] = [
{
TaskID: 1,
TaskName: 'Planning',
Duration: 5,
Children: [
{ TaskID: 2, TaskName: 'Plan timeline', Duration: 5 },
{ TaskID: 3, TaskName: 'Plan budget', Duration: 5 }
]
}
];
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
treeColumnIndex={1}
height="auto"
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
</TreeGridComponent>
);
}Hierarchical Data with childMapping
Bind data where child records are nested in a Children property:
const hierarchicalData = [
{
TaskID: 1,
TaskName: 'Planning',
StartDate: new Date(2018, 2, 3),
Children: [
{
TaskID: 2,
TaskName: 'Plan timeline',
StartDate: new Date(2018, 2, 3),
Children: [
{ TaskID: 21, TaskName: 'Identify project scope' }
]
},
{ TaskID: 3, TaskName: 'Plan budget' }
]
},
{ TaskID: 4, TaskName: 'Design' }
];
<TreeGridComponent
dataSource={hierarchicalData}
childMapping="Children"
treeColumnIndex={1}
>
{/* columns */}
</TreeGridComponent>Key Binding Properties:
dataSource: Your hierarchical data arraychildMapping="Children": Property name containing child recordstreeColumnIndex={1}: Column index to show tree (expand/collapse arrows)
Flat Data with parentID
Bind flat arrays by mapping a foreign key (parentID) to identify parent-child relationships:
const flatData = [
{ TaskID: 1, TaskName: 'Planning', ParentID: null },
{ TaskID: 2, TaskName: 'Plan timeline', ParentID: 1 },
{ TaskID: 3, TaskName: 'Plan budget', ParentID: 1 },
{ TaskID: 4, TaskName: 'Design', ParentID: null },
{ TaskID: 5, TaskName: 'UI Design', ParentID: 4 }
];
<TreeGridComponent
dataSource={flatData}
idMapping="TaskID"
parentIdMapping="ParentID"
treeColumnIndex={1}
>
{/* columns */}
</TreeGridComponent>Key Binding Properties:
idMapping="TaskID": Unique identifier for each recordparentIdMapping="ParentID": Foreign key pointing to parent ID (null for root records)
Remote Data Binding
Bind TreeGrid to server-side data using DataManager:
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective } from '@syncfusion/ej2-react-treegrid';
import { DataManager, UrlAdaptor } from '@syncfusion/ej2-data';
export default function App() {
const dataManager = new DataManager({
url: 'url',
adaptor: new UrlAdaptor(),
offline: false // Set true to cache response locally
});
return (
<TreeGridComponent
dataSource={dataManager}
idMapping="TaskID"
parentIdMapping="parentID"
treeColumnIndex={1}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
</TreeGridComponent>
);
}ExpandStateMapping
Control which records start expanded or collapsed via data property:
const data = [
{
TaskID: 1,
TaskName: 'Planning',
expandState: true, // This parent starts expanded
Children: [
{
TaskID: 2,
TaskName: 'Plan timeline',
expandState: false, // This parent starts collapsed
Children: [
{ TaskID: 21, TaskName: 'Identify scope' }
]
}
]
}
];
<TreeGridComponent
dataSource={data}
childMapping="Children"
expandStateMapping="expandState" // Maps to expandState property
>
{/* columns */}
</TreeGridComponent>Dynamic Data Loading
Update data source programmatically:
const treeGridRef = React.useRef();
const updateData = (newData) => {
treeGridRef.current.dataSource = newData;
treeGridRef.current.refresh();
};
const addRow = (parentData, newChildRecord) => {
if (!parentData.Children) {
parentData.Children = [];
}
parentData.Children.push(newChildRecord);
treeGridRef.current.refresh();
};
<TreeGridComponent ref={treeGridRef} dataSource={data} childMapping="Children">
{/* columns */}
</TreeGridComponent>---
Complex Data Binding
For nested objects and complex property mapping:
const data = [
{
TaskID: 1,
info: {
TaskName: 'Planning',
Category: 'Development'
},
metadata: {
StartDate: new Date(2018, 2, 3)
},
Children: [
{
TaskID: 2,
info: { TaskName: 'Plan timeline' },
metadata: { StartDate: new Date(2018, 2, 3) }
}
]
}
];
<TreeGridComponent
dataSource={data}
childMapping="Children"
treeColumnIndex={1}
>
<ColumnsDirective>
<ColumnDirective field="info.TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="metadata.StartDate" headerText="Start Date" type="date" width={120} />
</ColumnsDirective>
</TreeGridComponent>Note: Use dot notation for nested properties (e.g., info.TaskName)
Key APIs
| Property/Method | Type | Description |
|---|---|---|
dataSource | array \ | DataManager |
childMapping | string | Property containing child records |
idMapping | string | Unique identifier (for parentID binding) |
parentIdMapping | string | Foreign key to parent ID |
treeColumnIndex | number | Column showing tree structure |
expandByDefault | string | Property controlling expand/collapse state |
refresh | method | Refresh grid with updated data |
expanding | event | Fired before parent expands |
expanded | event | Fired after parent expands |
collapsing | event | Fired before parent collapses |
Common Patterns
1. Nested Hierarchy: Use childMapping for naturally nested data structures 2. Flat Normalization: Use parentID mapping for flattened hierarchical data 3. Dynamic Updates: Refresh data after add/edit/delete operations 4. Initial State: Use expandByDefault to show expanded hierarchy on load
const data = [
{
TaskID: 1,
info: {
TaskName: 'Planning',
Category: 'Development'
},
}
];
<TreeGridComponent
dataSource={data}
childMapping="Children"
expandStateMapping="expandState"
treeColumnIndex={1}
/>Use Case: Restore saved expand state or pre-expand specific hierarchy levels on load.
Editing
Table of Contents
- CRUD & Editing Rules
- Overview
- Cell Editing
- Row Editing
- Dialog Editing
- Batch Editing
- Template Editing
- Validation
- Key APIs
- Common Patterns
CRUD & Editing Rules
Rule 1: EditSettings is MANDATORY for Editing
Severity: 🔴 CRITICAL - Editing won't work without configuration
Requirement:
// ✅ REQUIRED - Must define editSettings
const editSettings = {
mode: 'Cell', // Required: 'Cell', 'Dialog', 'Row', 'Batch'
allowEditing: true, // Required: Enable editing
allowAdding: true, // Optional: Enable adding rows
allowDeleting: true // Optional: Enable deleting rows
};
<TreeGridComponent
dataSource={data}
editSettings={editSettings}
childMapping='subtasks'>
<ColumnsDirective>
{/* Columns */}
</ColumnsDirective>
<Inject services={[Edit]} /> {/* MUST also inject Edit module */}
</TreeGridComponent>
// ❌ WRONG - No editSettings = No editing possible
<TreeGridComponent
dataSource={data}>
{/* No editing functionality without editSettings */}
</TreeGridComponent>Additional Requirements:
// ✅ MUST inject Edit module for any editing mode
import { Edit } from '@syncfusion/ej2-react-treegrid';
<TreeGridComponent>
<Inject services={[Edit]} /> {/* REQUIRED */}
</TreeGridComponent>Edit Mode Rules:
// Cell Mode - Edit individual cells
editSettings={{ mode: 'Cell', allowEditing: true }}
// Dialog Mode - Edit in popup dialog
editSettings={{ mode: 'Dialog', allowEditing: true }}
// Row Mode - Edit full row inline
editSettings={{ mode: 'Row', allowEditing: true }}
// Batch Mode - Edit multiple rows, save all at once
editSettings={{ mode: 'Batch', allowEditing: true }}---
Rule 2: Inject Module is MANDATORY for Editing
Severity: 🔴 CRITICAL - Editing features won't initialize without Inject module
Requirement:
// ✅ REQUIRED - Edit module must be injected
import { Edit } from '@syncfusion/ej2-react-treegrid';
<TreeGridComponent
editSettings={editSettings}>
{/* ... */}
<Inject services={[Edit]} /> {/* MANDATORY for editing */}
</TreeGridComponent>
// ❌ WRONG - Missing Edit module injection
<TreeGridComponent
editSettings={editSettings}>
{/* ... */}
{/* No <Inject> = Editing fails */}
</TreeGridComponent>---
Rule 3: Column EditType Must Match Data Type
Severity: 🟠 IMPORTANT - Validation fails with mismatched editTypes
Requirement:
// ✅ CORRECT - EditType matches data type
<ColumnsDirective>
<ColumnDirective field='TaskName'
headerText='Task'
editType='defaultedit'></ColumnDirective>
<ColumnDirective field='Duration'
headerText='Duration'
editType='numericedit'
validationRules={{ required: true, min: 1, max: 1000 }}></ColumnDirective>
<ColumnDirective field='Priority'
headerText='Priority'
editType='dropdownedit'></ColumnDirective>
<ColumnDirective field='StartDate'
headerText='Start Date'
editType='datepickeredit'
format='yMd'></ColumnDirective>
<ColumnDirective field='IsCompleted'
headerText='Completed'
editType='booleanedit'></ColumnDirective>
</ColumnsDirective>
// ❌ WRONG - EditType mismatch
<ColumnDirective field='Duration'
headerText='Duration'
type='number'
editType='defaultedit' {/* Should be numericedit */}
></ColumnDirective>Valid EditTypes:
| EditType | Best For | Validation |
|---|---|---|
| defaultedit | String fields | max length |
| numericedit | Numbers | min, max, decimals |
| dropdownedit | Fixed options | required |
| datepickeredit | Dates | date range |
| booleanedit | Booleans | true/false |
| datetimepickeredit | Date and Time | date and time range |
---
Rule 4: Primary Key (isPrimaryKey) is MANDATORY for CRUD Operations
Severity: 🔴 CRITICAL - Editing/Deleting fails silently without this
Requirement:
// ✅ REQUIRED - Exactly ONE column must have isPrimaryKey={true}
<ColumnsDirective>
<ColumnDirective field='TaskID' headerText='ID' isPrimaryKey={true}></ColumnDirective>
<ColumnDirective field='TaskName' headerText='Task'></ColumnDirective>
</ColumnsDirective>
// ❌ WRONG - No primary key = CRUD operations fail silently
<ColumnsDirective>
<ColumnDirective field='TaskID' headerText='ID'></ColumnDirective>
<ColumnDirective field='TaskName' headerText='Task'></ColumnDirective>
</ColumnsDirective>Rules:
- ✅ Only ONE primary key allowed per grid
- ✅ Primary key value must be UNIQUE for each row
- ✅ Primary key must not be NULL/undefined
- ✅ Must match a field in data source
- ❌ Do NOT mark multiple columns as primary key
- ❌ Do NOT use composite/multi-column primary keys
Impact Without This:
// What fails without isPrimaryKey:
- Edit operations (beginEdit fails silently)
- Delete operations (no row deleted)
- Update operations (data not persisted)
- Row selection preservation
- State persistence---
Overview
TreeGrid supports multiple editing modes for updating hierarchical data with proper CRUD operations.
Cell Editing
Edit data inline at the cell level:
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, Edit, Toolbar } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{ TaskID: 1, TaskName: 'Planning', Priority: 'High', Children: [] }
];
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
editSettings={{ mode: 'Cell', allowEditing: true, allowDeleting: true, allowAdding: true }}
toolbar={['Add', 'Edit', 'Delete', 'Update', 'Cancel']}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} type="number" allowEditing={false} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Priority" headerText="Priority" width={100} editType="dropdownedit" />
<ColumnDirective type="checkbox" width={50} />
</ColumnsDirective>
<Inject services={[Edit, Toolbar]} />
</TreeGridComponent>
);
}Row Editing
Edit entire rows in a dialog or inline form:
<TreeGridComponent
dataSource={data}
childMapping="Children"
editSettings={{ mode: 'Row', allowEditing: true, allowDeleting: true }}
toolbar={['Edit', 'Delete', 'Update', 'Cancel']}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} type="number" />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
<Inject services={[Edit, Toolbar]} />
</TreeGridComponent>Dialog Editing
Edit data in a modal dialog:
<TreeGridComponent
dataSource={data}
childMapping="Children"
editSettings={{
mode: 'Dialog',
allowEditing: true,
allowAdding: true,
allowDeleting: true,
newRowPosition: 'Child'
}}
toolbar={['Add', 'Edit', 'Delete', 'Update', 'Cancel']}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
<Inject services={[Edit, Toolbar]} />
</TreeGridComponent>Batch Editing
Edit multiple rows and save in batch:
<TreeGridComponent
dataSource={data}
childMapping="Children"
editSettings={{
mode: 'Batch',
allowEditing: true,
allowAdding: true,
allowDeleting: true
}}
toolbar={['Add', 'Edit', 'Delete', 'Update', 'Cancel']}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
<ColumnDirective type="checkbox" width={50} />
</ColumnsDirective>
<Inject services={[Edit, Toolbar]} />
</TreeGridComponent>Template Editing
Use custom templates for edit forms:
const editTemplate = (props) => {
return (
<div>
<label>Task Name:</label>
<input value={props.TaskName} defaultValue={props.TaskName} />
</div>
);
};
<TreeGridComponent
dataSource={data}
childMapping="Children"
editSettings={{ mode: 'Cell', allowEditing: true }}
>
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" editTemplate={editTemplate} />
</ColumnsDirective>
</TreeGridComponent>Validation
Add validation rules to edited data:
<TreeGridComponent
dataSource={data}
childMapping="Children"
editSettings={{ mode: 'Dialog', allowEditing: true }}
>
<ColumnsDirective>
<ColumnDirective
field="TaskName"
headerText="Task Name"
validationRules={{ required: true, min: 3 }}
/>
<ColumnDirective
field="Priority"
headerText="Priority"
validationRules={{ required: true }}
/>
</ColumnsDirective>
<Inject services={[Edit, Toolbar]} />
</TreeGridComponent>Key APIs
| Property/Event | Type | Description |
|---|---|---|
editSettings | object | Configure edit mode and behavior |
mode | string | 'Cell', 'Row', 'Dialog', 'Batch' |
allowEditing | boolean | Enable row/cell editing |
allowAdding | boolean | Allow adding new rows |
allowDeleting | boolean | Allow deleting rows |
newRowPosition | string | 'Top', 'Bottom', 'Child' for new rows |
validationRules | object | Validation rules per column |
actionComplete | event | Fired after edit action completes |
actionFailure | event | Fired when edit action fails |
Common Patterns
1. Server Persistence: Handle actionComplete to save to server 2. Parent-Child Add: Use newRowPosition: 'Child' for adding child records 3. Custom Validators: Implement custom validation logic in validationRules 4. Batch Operations: Collect edits in batch mode before saving
Events Reference (Events Catalog)
Table of Contents
- Event Handling Rules
- Core Action Events
- Data Events
- Row Events
- Cell Events
- Column Events
- Editing Events
- Selection Events
- Expand/Collapse Events
- Drag & Drop Events
- RequestType Values
- Tree-Specific Events
- Event Arguments Reference
- Complete Event Example
Event Handling Rules
Rule 1: Event Names Follow Specific camelCase Pattern
Severity: 🟠 IMPORTANT - Event names must match exactly (case-sensitive)
Correct Event Names in React:
// ✅ CORRECT - Exact event names (camelCase)
<TreeGridComponent
onRowSelecting={(args) => handleRowSelecting(args)}
onRowSelected={(args) => handleRowSelected(args)}
onRowDeselecting={(args) => handleRowDeselecting(args)}
onRowDeselected={(args) => handleRowDeselected(args)}
onRowDragStart={(args) => handleRowDragStart(args)}
onRowDrag={(args) => handleRowDrag(args)}
onRowDrop={(args) => handleRowDrop(args)}
onRowDragStop={(args) => handleRowDragStop(args)}
onDataBound={(args) => handleDataBound(args)}
onDataSourceChanged={(args) => handleDataSourceChanged(args)}
onDataStateChange={(args) => handleDataStateChange(args)}
onActionBegin={(args) => handleActionBegin(args)}
onActionComplete={(args) => handleActionComplete(args)}
onActionFailure={(args) => handleActionFailure(args)}
onQueryCellInfo={(args) => handleQueryCellInfo(args)}
onRowDataBound={(args) => handleRowDataBound(args)}
onRecordDoubleClick={(args) => handleRecordDoubleClick(args)}
onCellSave={(args) => handleCellSave(args)}>
</TreeGridComponent>
// ❌ WRONG - Incorrect event names (won't fire)
<TreeGridComponent
onrowdragstart={handleRowDragStart} // lowercase 'r' and 'd' - WON'T FIRE
ondragStart={handleDragStart} // Should be onRowDragStart - WON'T FIRE
onRowDragStart={handleRowDragStart} // Correct format
ondatabound={handleDataBound} // Should be onDataBound - WON'T FIRE
onDataBound={handleDataBound} // Correct format
/>Event Handler Patterns in React:
// ✅ CORRECT - Event handlers with type annotations
const handleRowSelecting = (args: RowSelectingEventArgs) => {
console.log('Row selecting:', args.rowIndex);
};
const handleActionComplete = (args: ActionCompleteEventArgs) => {
if (args.requestType === 'save') {
console.log('Record saved:', args.data);
}
};
<TreeGridComponent
onRowSelecting={handleRowSelecting}
onActionComplete={handleActionComplete}>
</TreeGridComponent>
// ✅ ALTERNATIVE - Inline arrow functions
<TreeGridComponent
onRowSelecting={(args) => {
console.log('Selected:', args.rowIndex);
}}
onActionComplete={(args) => {
if (args.requestType === 'save') {
// Handle save
}
}}>
</TreeGridComponent>---
Rule 2: Event Handler State Updates in CRUD Operations
Severity: 🟠 IMPORTANT - Async operations require proper state handling
Requirement in React:
// ✅ CORRECT - Handle async CRUD with proper state update
import { useState } from 'react';
export default function GridWithCRUD() {
const [data, setData] = useState(initialData);
// Handle data source changes (CRUD operations)
const handleDataSourceChanged = async (args) => {
if (args.action === 'add') {
try {
// Make API call
const response = await fetch('/api/tasks', {
method: 'POST',
body: JSON.stringify(args.data)
});
const newRecord = await response.json();
// Update state with server response
setData(prevData => [...prevData, newRecord]);
// Refresh grid
gridRef.current?.refresh();
} catch (error) {
console.error('Add failed:', error);
}
}
else if (args.action === 'edit') {
try {
await fetch(`/api/tasks/${args.data.TaskID}`, {
method: 'PUT',
body: JSON.stringify(args.data)
});
// Update state
setData(prevData =>
prevData.map(item =>
item.TaskID === args.data.TaskID ? args.data : item
)
);
} catch (error) {
console.error('Edit failed:', error);
}
}
else if (args.action === 'delete') {
try {
await fetch(`/api/tasks/${args.data[0].TaskID}`, {
method: 'DELETE'
});
// Update state
setData(prevData =>
prevData.filter(item => item.TaskID !== args.data[0].TaskID)
);
} catch (error) {
console.error('Delete failed:', error);
}
}
};
return (
<TreeGridComponent
dataSource={data}
editSettings={{ mode: 'Dialog', allowEditing: true, allowAdding: true, allowDeleting: true }}
onActionComplete={handleDataSourceChanged}>
<ColumnsDirective>
<ColumnDirective field='TaskID' isPrimaryKey={true}></ColumnDirective>
</ColumnsDirective>
<Inject services={[Edit]} />
</TreeGridComponent>
);
}
// ❌ WRONG - Not updating state after CRUD
const handleActionComplete = (args) => {
if (args.action === 'add') {
fetch('/api/tasks', { method: 'POST', body: JSON.stringify(args.data) });
// Missing: Update state! Grid won't reflect changes.
}
};
// ❌ WRONG - Mutating state directly
const handleActionComplete = (args) => {
if (args.action === 'add') {
data.push(args.data); // DON'T mutate directly!
// Should use: setData([...data, args.data])
}
};---
Rule 3: Event Args Validation and Error Prevention
Severity: 🟠 IMPORTANT - Validate event arguments to prevent silent failures
Requirement in React:
// ✅ CORRECT - Validate and prevent default behavior when needed
<TreeGridComponent
onRowSelecting={(args) => {
// Validate before allowing selection
if (!isValidRow(args.rowData)) {
args.cancel = true; // ✅ Prevent selection
}
}}
onBeginEdit={(args) => {
// Check if user has permission
if (!hasEditPermission(args.rowData)) {
args.cancel = true; // ✅ Prevent edit
}
}}
onBeginDelete={(args) => {
// Show confirmation
if (!confirm(`Delete row ${args.index}?`)) {
args.cancel = true; // ✅ Prevent deletion
}
}}>
</TreeGridComponent>
// ✅ ALTERNATIVE - Use actionBegin for multi-step validation
const handleActionBegin = (args) => {
if (args.requestType === 'save') {
// Validation before save
if (!validateRecord(args.data)) {
args.cancel = true; // ✅ Cancel save
alert('Please fix validation errors');
return;
}
}
};
<TreeGridComponent onActionBegin={handleActionBegin} />
// ❌ WRONG - Allowing invalid operations
const handleRowSelecting = (args) => {
if (!isValidRow(args.rowData)) {
// Missing: args.cancel = true
// Row will be selected even though it's invalid!
}
};
// ❌ WRONG - Not handling async properly
const handleBeginDelete = async (args) => {
const confirmed = await showConfirmationDialog();
if (!confirmed) {
args.cancel = true; // May not work as expected
}
};---
Property Naming Rules
Rule 1: Property Names are Case-Sensitive in React
Severity: 🔴 CRITICAL - Case mismatch causes silent failures (properties just ignored)
Correct Property Names in React (camelCase):
// ✅ CORRECT - Exact casing (camelCase for properties)
<TreeGridComponent
allowPaging={true}
allowSorting={true}
allowFiltering={true}
allowRowDragAndDrop={true} // Capital 'R', 'D', 'A', capital 'D' in second word
enableVirtualization={true}
enableInfiniteScrolling={true}
enablePersistence={true}
enableAdaptiveUI={true} // Capital 'A' and 'U'
showColumnMenu={true}
childMapping='subtasks'
idMapping='TaskID'
parentIdMapping='ParentID'
hasChildMapping='IsParent'>
</TreeGridComponent>
// ❌ WRONG - Incorrect casing (property is silently ignored!)
<TreeGridComponent
allowpaging={true} // lowercase 'p' - IGNORED
allowsorting={true} // lowercase 's' - IGNORED
allowfiltering={true} // lowercase 'f' - IGNORED
allowRowDragandDrop={true} // lowercase 'a' in 'and' - IGNORED!
enablevirtualization={true} // lowercase 'v' - IGNORED
childmapping='subtasks' // lowercase 'c' and 'm' - IGNORED
idmapping='TaskID' // lowercase 'i' and 'm' - IGNORED
/>Column Property Casing (Must Match Exactly):
// ✅ CORRECT - Exact camelCase for column properties
<ColumnDirective
isPrimaryKey={true} // Capital 'I' and 'P'
isFrozen={true}
isIdentity={true}
allowSorting={true} // Capital 'A' and 'S'
allowFiltering={true}
allowSearching={true}
enableGrouping={true}
displayAsCheckBox={true} // Capital 'D', 'A', 'C', 'B'
allowReordering={true}
allowResizing={true}
showColumnMenu={true}
/>
// ❌ WRONG - Will be ignored if casing is wrong
<ColumnDirective
isprimarykey={true} // ALL lowercase - IGNORED
isfrozen={true}
allowsorting={true} // Wrong casing - IGNORED
displayascheckbox={true} // Wrong casing - IGNORED
/>Common Case Errors:
// WRONG: These will silently fail
allowRowDragandDrop={true} // Should be allowRowDragAndDrop (capital A, D, D)
enableInfiniteScrolling={true} // Not enableInfiniteScrollinG
enableadaptiveUI={true} // Not enableAdaptiveUI (capital A, U)
ShowColumnMenu={true} // Not showColumnMenu (lowercase s)
AllowPaging={true} // Not allowPaging (lowercase a, p)
// CORRECT: These work
allowRowDragAndDrop={true}
enableInfiniteScrolling={true}
enableAdaptiveUI={true}
showColumnMenu={true}
allowPaging={true}---
Event Communication Pattern
TreeGrid uses a three-phase event system:
1. actionBegin (Before) - Cancel or mutate data before action 2. actionComplete (After) - React after successful action 3. actionFailure (Error) - Handle failures
Core Action Events
actionBegin
Phase: Before action executes Type: ActionEventArgs Purpose: Validate, cancel, or modify data before action
const handleActionBegin = (args: ActionEventArgs) => {
console.log('Action Type:', args.requestType);
// Cancel action conditionally
if (args.requestType === 'delete' && !canDelete(args.data)) {
args.cancel = true;
toast.error('Cannot delete this item');
}
// Modify data before save
if (args.requestType === 'save') {
args.data.modifiedDate = new Date();
args.data.modifiedBy = currentUser.id;
}
};
<TreeGridComponent actionBegin={handleActionBegin} />actionComplete
Phase: After action completes successfully Type: ActionEventArgs Purpose: React to successful action, sync with server, update UI
const handleActionComplete = (args: ActionEventArgs) => {
if (args.requestType === 'save') {
// Sync to server
syncToServer(args.data)
.then(() => toast.success('Saved successfully'))
.catch(err => console.error(err));
}
if (args.requestType === 'delete') {
toast.info('Item deleted');
refreshAnalytics();
}
if (args.requestType === 'expand') {
// Track expand events
trackEvent('tree-node-expanded', { id: args.data.TaskID });
}
};
<TreeGridComponent actionComplete={handleActionComplete} />actionFailure
Phase: When action fails Type: FailureEventArgs Purpose: Handle errors, show messages, rollback
const handleActionFailure = (args: FailureEventArgs) => {
console.error('TreeGrid Error:', args.error);
if (args.error?.status === 401) {
redirectToLogin();
} else if (args.error?.status === 403) {
toast.error('Permission denied');
} else {
toast.error(`Error: ${args.error?.message || 'Unknown error'}`);
}
};
<TreeGridComponent actionFailure={handleActionFailure} />requestType Values
Use args.requestType to identify the action:
CRUD Operations
| requestType | Trigger | Available in actionBegin | Available in actionComplete |
|---|---|---|---|
save | Row saved after edit | ✅ Yes (can cancel) | ✅ Yes |
add | New row added | ✅ Yes (can cancel) | ✅ Yes |
delete | Row deleted | ✅ Yes (can cancel) | ✅ Yes |
beginEdit | Edit started | ✅ Yes (can cancel) | ✅ Yes |
cancel | Edit cancelled | ❌ No | ✅ Yes |
Tree Operations
| requestType | Trigger | Available in actionBegin | Available in actionComplete |
|---|---|---|---|
expand | Node expanded | ✅ Yes (can cancel) | ✅ Yes |
collapse | Node collapsed | ✅ Yes (can cancel) | ✅ Yes |
Data Operations
| requestType | Trigger | Available in actionBegin | Available in actionComplete |
|---|---|---|---|
sorting | Column sorted | ✅ Yes (can cancel) | ✅ Yes |
filtering | Filter applied | ✅ Yes (can cancel) | ✅ Yes |
searching | Search performed | ✅ Yes (can cancel) | ✅ Yes |
paging | Page changed | ✅ Yes (can cancel) | ✅ Yes |
refresh | Grid refreshed | ❌ No | ✅ Yes |
Tree-Specific Events
expanding
Phase: Before node expands Type: RowExpandingEventArgs Purpose: Lazy load children, validate before expand
const handleExpanding = (args: RowExpandingEventArgs) => {
const node = args.data;
// Lazy load children if not already loaded
if (!node.Children || node.Children.length === 0) {
args.cancel = true; // Cancel default expand
fetchChildren(node.TaskID).then(children => {
node.Children = children;
treeGridRef.current.refresh();
});
}
};
<TreeGridComponent expanding={handleExpanding} />expanded
Phase: After node expands Type: RowExpandedEventArgs Purpose: Track state, analytics
const handleExpanded = (args: RowExpandedEventArgs) => {
// Save expand state
saveExpandState(args.data.TaskID, true);
// Track analytics
trackEvent('node-expanded', { id: args.data.TaskID });
};
<TreeGridComponent expanded={handleExpanded} />collapsing
Phase: Before node collapses Type: RowCollapsingEventArgs Purpose: Confirm unsaved changes
const handleCollapsing = (args: RowCollapsingEventArgs) => {
if (hasUnsavedChanges(args.data)) {
args.cancel = true;
showConfirmDialog('You have unsaved changes. Collapse anyway?')
.then(confirmed => {
if (confirmed) {
discardChanges(args.data);
treeGridRef.current.collapseRow(args.row);
}
});
}
};
<TreeGridComponent collapsing={handleCollapsing} />collapsed
Phase: After node collapses Type: RowCollapsedEventArgs Purpose: Track state, cleanup
const handleCollapsed = (args: RowCollapsedEventArgs) => {
// Save collapse state
saveExpandState(args.data.TaskID, false);
};
<TreeGridComponent collapsed={handleCollapsed} />Selection Events
rowSelecting
Phase: Before row selection Type: RowSelectingEventArgs Purpose: Conditional selection, validation
const handleRowSelecting = (args: RowSelectingEventArgs) => {
// Prevent selection of certain rows
if (args.data.isDisabled) {
args.cancel = true;
toast.info('This item cannot be selected');
}
};
<TreeGridComponent rowSelecting={handleRowSelecting} />rowSelected
Phase: After row selected Type: RowSelectEventArgs Purpose: Update UI, fetch details, navigation
const handleRowSelected = (args: RowSelectEventArgs) => {
const selectedData = args.data;
// Fetch additional details
fetchTaskDetails(selectedData.TaskID)
.then(details => setSelectedTaskDetails(details));
// Update URL or navigate
navigate(`/tasks/${selectedData.TaskID}`);
};
<TreeGridComponent rowSelected={handleRowSelected} />rowDeselecting / rowDeselected
Similar pattern for deselection events.
Cell Events
cellEdit
Phase: Before cell edit starts Type: CellEditArgs Purpose: Custom validation, conditional edit
const handleCellEdit = (args: CellEditArgs) => {
// Prevent editing certain cells
if (args.columnName === 'Status' && args.rowData.isLocked) {
args.cancel = true;
}
};
<TreeGridComponent cellEdit={handleCellEdit} />cellSave
Phase: Before cell value saves Type: CellSaveArgs Purpose: Transform data, validate
const handleCellSave = (args: CellSaveArgs) => {
// Transform value before save
if (args.columnName === 'TaskName') {
args.value = args.value.trim().toUpperCase();
}
// Validate
if (args.columnName === 'Duration' && args.value < 0) {
args.cancel = true;
toast.error('Duration cannot be negative');
}
};
<TreeGridComponent cellSave={handleCellSave} />cellSaved
Phase: After cell value saved Type: CellSaveArgs Purpose: React to save, update related fields
const handleCellSaved = (args: CellSaveArgs) => {
// Update related calculations
if (args.columnName === 'Duration') {
recalculateEndDate(args.rowData);
}
};
<TreeGridComponent cellSaved={handleCellSaved} />Render Events
rowDataBound
Phase: During row render Type: RowDataBoundEventArgs Purpose: Custom row styling (NO API CALLS)
⚠️ WARNING: Fires on every render. Do NOT make API calls here!
const handleRowDataBound = (args: RowDataBoundEventArgs) => {
// ✅ CORRECT - Styling only
if (args.data.Priority === 'High') {
args.row.classList.add('high-priority-row');
}
// ❌ WRONG - API call will fire constantly!
// fetch(`/api/status/${args.data.id}`).then(...); // DON'T DO THIS!
};
<TreeGridComponent rowDataBound={handleRowDataBound} />queryCellInfo
Phase: During cell render Type: QueryCellInfoEventArgs Purpose: Custom cell styling (NO API CALLS)
⚠️ WARNING: Fires on every cell render. Do NOT make API calls here!
const handleQueryCellInfo = (args: QueryCellInfoEventArgs) => {
// ✅ CORRECT - Styling based on existing data
if (args.column.field === 'Status') {
if (args.data.Status === 'Completed') {
args.cell.style.backgroundColor = '#d4edda';
} else if (args.data.Status === 'Blocked') {
args.cell.style.backgroundColor = '#f8d7da';
}
}
// ❌ WRONG - API call
// fetchCellData(args.data.id).then(...); // DON'T DO THIS!
};
<TreeGridComponent queryCellInfo={handleQueryCellInfo} />Data Events
dataSourceChanged
Phase: After dataSource changes Type: DataSourceChangedEventArgs Purpose: React to data changes
const handleDataSourceChanged = (args: DataSourceChangedEventArgs) => {
console.log('Data source updated');
updateAnalytics();
};
<TreeGridComponent dataSourceChanged={handleDataSourceChanged} />dataBound
Phase: After data binding completes Type: Object Purpose: Post-render operations
const handleDataBound = () => {
console.log('Data binding complete');
// Safe to perform operations after initial render
applyCustomizations();
};
<TreeGridComponent dataBound={handleDataBound} />Complete Event Example
import React, { useRef } from 'react';
import { TreeGridComponent, Inject, Edit, Toolbar } from '@syncfusion/ej2-react-treegrid';
import { ActionEventArgs, FailureEventArgs } from '@syncfusion/ej2-treegrid';
function TreeGridWithEvents() {
const treeGridRef = useRef<TreeGridComponent>(null);
const handleActionBegin = (args: ActionEventArgs) => {
// Validation and mutation before action
if (args.requestType === 'save') {
if (!args.data.TaskName) {
args.cancel = true;
toast.error('Task name is required');
return;
}
args.data.modifiedDate = new Date();
}
if (args.requestType === 'delete') {
if (args.data.Children?.length > 0) {
args.cancel = true;
toast.error('Cannot delete item with children');
}
}
};
const handleActionComplete = (args: ActionEventArgs) => {
// React after successful action
if (args.requestType === 'save') {
syncToServer(args.data).catch(err => {
console.error('Sync failed:', err);
toast.error('Failed to sync with server');
});
}
if (args.requestType === 'delete') {
toast.success('Item deleted successfully');
}
};
const handleActionFailure = (args: FailureEventArgs) => {
console.error('Action failed:', args.error);
toast.error(`Error: ${args.error?.message || 'Unknown error'}`);
};
const handleExpanding = (args) => {
// Lazy load children
if (args.data.hasChildren && !args.data.Children) {
args.cancel = true;
fetchChildren(args.data.TaskID).then(children => {
args.data.Children = children;
treeGridRef.current.refresh();
});
}
};
return (
<TreeGridComponent
ref={treeGridRef}
dataSource={data}
childMapping="Children"
treeColumnIndex={1}
editSettings={{ allowEditing: true, allowAdding: true, allowDeleting: true }}
toolbar={['Add', 'Edit', 'Delete', 'Update', 'Cancel']}
actionBegin={handleActionBegin}
actionComplete={handleActionComplete}
actionFailure={handleActionFailure}
expanding={handleExpanding}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="ID" isPrimaryKey={true} />
<ColumnDirective field="TaskName" headerText="Task Name" />
</ColumnsDirective>
<Inject services={[Edit, Toolbar]} />
</TreeGridComponent>
);
}Best Practices
1. Always handle actionFailure for error recovery 2. Use actionBegin for validation and cancellation 3. Use actionComplete for side effects (API calls, notifications) 4. Check requestType to identify specific actions 5. Never make API calls in rowDataBound or queryCellInfo 6. Use refs for imperative operations after events 7. Memoize event handlers with useCallback for performance
Excel Export
Table of Contents
Basic Excel Export
Export TreeGrid to Excel:
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, ExcelExport, Toolbar } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{ TaskID: 1, TaskName: 'Planning', Budget: 10000, Children: [] }
];
const toolbarClick = (args) => {
if (treegrid && args.item.text === 'Excel Export') {
treegrid.excelExport();
}
};
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
toolbar={['ExcelExport']}
allowExcelExport = {true}
toolbarClick={toolbarClick}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Budget" headerText="Budget" width={120} format="N2" />
</ColumnsDirective>
<Inject services={[ExcelExport, Toolbar]} />
</TreeGridComponent>
);
}Configure Excel export:
const treeGridRef = React.useRef();
const exportExcel = () => {
const excelExportProperties = {
fileName: 'treegrid.xlsx',
dataSource: data,
columns: [
{ field: 'TaskID', headerText: 'Task ID', width: 80 },
{ field: 'TaskName', headerText: 'Task Name', width: 160 },
{ field: 'Budget', headerText: 'Budget', width: 120 }
]
};
treeGridRef.current.excelExport(excelExportProperties);
};
<TreeGridComponent ref={treeGridRef} dataSource={data} childMapping="Children" allowExcelExport = {true}
toolbarClick={toolbarClick}>
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
<Inject services={[ExcelExport]} />
</TreeGridComponent>Headers and Footers
Add headers and footers to Excel:
const excelExportProperties = {
fileName: 'treegrid.xlsx',
header: {
rows: [
{
cells: [
{
value: 'Project Planning',
colSpan: 6,
style: { bold: true, fontSize: 20 }
}
]
}
]
},
footer: {
rows: [
{
cells: [
{
value: 'Total Records: ' + data.length,
colSpan: 6
}
]
}
]
}
};
treeGridRef.current.excelExport(excelExportProperties);Cell Formatting
Format cells in export:
import { getValue } from '@syncfusion/ej2-base';
import { ColumnDirective, ColumnsDirective, Page, TreeGridComponent } from '@syncfusion/ej2-react-treegrid';
import { ExcelExport, Inject, Toolbar } from '@syncfusion/ej2-react-treegrid';
import * as React from 'react';
import { sampleData } from './datasource';
function App() {
const toolbarOptions = ['ExcelExport'];
const pageSettings = { pageSize: 7 };
let treegrid;
const toolbarClick = (args) => {
if (treegrid && args.item.text === 'Excel Export') {
treegrid.excelExport();
}
};
const excelQueryCellInfo = (args) => {
if (args.column.field === 'duration') {
if (getValue('data.duration', args) === 0) {
args.style = { backColor: '#336c12' };
}
else if (getValue('data.duration', args) < 3) {
args.style = { backColor: '#7b2b1d' };
}
}
};
const queryCellInfo = (args) => {
if (args.column.field === 'duration') {
if (getValue('data.duration', args) === 0) {
args.cell.style.background = '#336c12';
}
else if (getValue('data.duration', args) < 3) {
args.cell.style.background = '#7b2b1d';
}
}
};
return <TreeGridComponent dataSource={sampleData} treeColumnIndex={1} childMapping='subtasks' allowPaging={true} pageSettings={pageSettings} allowExcelExport={true} height='220' toolbarClick={toolbarClick} ref={g => treegrid = g} toolbar={toolbarOptions} queryCellInfo={queryCellInfo} excelQueryCellInfo={excelQueryCellInfo}>
<ColumnsDirective>
<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, Toolbar, ExcelExport]}/>
</TreeGridComponent>;
}
;
export default App;Server-side Export
Export via server for large datasets:
const toolbarClick = (args) => {
if (grid && args.item.id === 'Treegrid_excelexport') {
grid.serverExcelExport('Home/ExcelExport');
}
};Key APIs
| Property | Type | Description |
|---|---|---|
excelExport | method | Export to Excel |
fileName | string | Output file name |
dataSource | array/object | Data to export |
columns | array | Columns to export |
header | object | Header configuration |
footer | object | Footer configuration |
customFormat | string | Cell number format |
Common Patterns
1. Hierarchical Export: Export with tree structure preserved 2. Filtered Export: Export only visible rows 3. Styled Export: Include colors, fonts, cell styles
Filtering
Table of Contents
- Enable Filtering
- Filter Bar
- Filter Menu
- Excel-like Filtering
- Filter Events
- Programmatic Filtering
- Custom Filters
- Key APIs
- Common Patterns
Enable Filtering
Enable filtering on TreeGrid to allow users to filter data:
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, Filter } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{ TaskID: 1, TaskName: 'Planning', Priority: 'High', Status: 'In Progress', Children: [] }
];
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
allowFiltering={true}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} allowFiltering={true} />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
<Inject services={[Filter]} />
</TreeGridComponent>
);
}Disable Filtering on Specific Column
Prevent filtering on individual columns:
<ColumnDirective
field="TaskID"
headerText="Task ID"
allowFiltering={false}
width={80}
/>Filter Bar
Display filter input box in column headers:
Basic Filter Bar
<TreeGridComponent
dataSource={data}
childMapping="Children"
allowFiltering={true}
filterSettings={{ type: 'FilterBar' }}
>
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
<Inject services={[Filter]} />
</TreeGridComponent>Filter Menu
Dropdown filter menu in column headers:
Filter Menu Configuration
<TreeGridComponent
dataSource={data}
childMapping="Children"
allowFiltering={true}
filterSettings={{ type: 'Menu' }}
>
<ColumnsDirective>
<ColumnDirective field="Priority" headerText="Priority" width={100} filter={{ type: 'Menu' }} />
</ColumnsDirective>
<Inject services={[Filter]} />
</TreeGridComponent>Excel-like Filtering
Excel-style filter interface with advanced options:
Excel Filter Configuration
<TreeGridComponent
dataSource={data}
childMapping="Children"
allowFiltering={true}
filterSettings={{ type: 'Excel' }}
>
<ColumnsDirective>
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
<Inject services={[Filter]} />
</TreeGridComponent>Filter Events
Action Complete Event
Detect when filter action completes:
<TreeGridComponent
dataSource={data}
childMapping="Children"
actionComplete={(args) => {
if (args.requestType === 'filtering') {
console.log('Filtering completed');
}
}}
>
{/* columns */}
</TreeGridComponent>Programmatic Filtering
Filter by Column
Apply filter from code:
const treeGridRef = React.useRef();
const filterByPriority = (priority) => {
treeGridRef.current.filterByColumn('Priority', 'equal', priority);
};
const filterByText = (text) => {
treeGridRef.current.filterByColumn('TaskName', 'contains', text);
};
<TreeGridComponent ref={treeGridRef} dataSource={data} childMapping="Children">
<ColumnsDirective>
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
</TreeGridComponent>Clear Filtering
Remove all filters:
treeGridRef.current.clearFiltering();Get Filter Settings
Retrieve current filter configuration:
const filterColumns = treeGridRef.current.filterSettings.columns;
console.log('Current filters:', filterColumns);Custom Filters
Implement Custom Filter Logic
Create filters with custom operators:
Custom Filters
Implement Custom Filter Logic
Create filters with custom operators:
<TreeGridComponent
dataSource={data}
childMapping="Children"
filterSettings={{
type: 'FilterBar',
columns: [
{
field: 'Priority',
operator: 'equal',
value: 'High'
}
]
}}
>
<ColumnsDirective>
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
<Inject services={[Filter]} />
</TreeGridComponent>Filter Operators
Common filter operators:
equal- Exact matchnotequal- Not equal tocontains- Contains textstartswith- Starts withendswith- Ends withlessthan- Less thangreaterthan- Greater thanbetween- Between values
---
Key APIs
| Property/Method | Type | Description |
|---|---|---|
allowFiltering | boolean | Enable filtering on TreeGrid |
filterSettings | object | Configure filter type and columns |
type | string | 'FilterBar', 'Menu', 'Excel' |
columns | array | Array of filter column definitions |
filterByColumn | method | Apply filter to specific column |
clearFiltering | method | Remove all filters |
filterChange | event | Fired when filter changes |
actionComplete | event | Fired when filter action completes |
Common Patterns
1. Multi-column Filtering: Combine filters across multiple columns 2. Date Range Filters: Filter between date ranges 3. Contains Filters: Case-insensitive partial match filters 4. Dynamic Filters: Update filters based on user input
Frozen Rows and Columns
Table of Contents
Freeze Rows
Freeze rows from the top:
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{ TaskID: 1, TaskName: 'Planning', Children: [] },
// ... many records
];
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
height={400}
frozenRows={1} // Freeze first row
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
</TreeGridComponent>
);
}Freeze Columns
Freeze columns from the left:
<TreeGridComponent
dataSource={data}
childMapping="Children"
height={400}
frozenColumns={1} // Freeze first column
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} isFrozen={true} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="StartDate" headerText="Start Date" width={120} />
</ColumnsDirective>
</TreeGridComponent>Lock Specific Columns
Lock individual columns:
<TreeGridComponent
dataSource={data}
childMapping="Children"
height={400}
>
<ColumnsDirective>
<ColumnDirective
field="TaskID"
headerText="Task ID"
width={80}
lockColumn={true}
/>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
</TreeGridComponent>Freeze Direction
Configure freeze direction for columns:
<TreeGridComponent
dataSource={data}
childMapping="Children"
height='310'
>
<ColumnsDirective>
<ColumnDirective field='taskID' headerText='Task ID' width='110' textAlign='Right'></ColumnDirective>
<ColumnDirective field='taskName' headerText='Task Name' width='230' freeze='Left'></ColumnDirective>
<ColumnDirective field='priority' headerText='Priority' width='110' freeze='Right'></ColumnDirective>
</ColumnsDirective>
</TreeGridComponent>;
Key APIs
| Property | Type | Description |
|---|---|---|
frozenRows | number | Number of rows to freeze from top |
frozenColumns | number | Number of columns to freeze from left |
isFrozen | boolean | Freeze specific column |
lockColumn | boolean | Lock column from dragging |
Common Patterns
1. Freeze ID Column: Keep identifier visible when scrolling 2. Fixed Headers: Freeze header row for navigation 3. Multi-freeze: Combine frozen rows and columns for complex layouts
Table of Contents
Basic Print
import React from 'react';
import { TreeGridComponent, ColumnsDirective, ColumnDirective, Inject, Print, Toolbar } from '@syncfusion/ej2-react-treegrid';
export default function App() {
const data = [
{ TaskID: 1, TaskName: 'Planning', Priority: 'High', Children: [] }
];
return (
<TreeGridComponent
dataSource={data}
childMapping="Children"
toolbar={['Print']}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" headerText="Task ID" width={80} />
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
<ColumnDirective field="Priority" headerText="Priority" width={100} />
</ColumnsDirective>
<Inject services={[Print, Toolbar]} />
</TreeGridComponent>
);
}Custom Print Configuration
Programmatic printing with options:
const treeGridRef = React.useRef();
const customPrint = () => {
treeGridRef.current.print();
};
<TreeGridComponent ref={treeGridRef} dataSource={data} childMapping="Children">
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
<Inject services={[Print]} />
</TreeGridComponent>Print Specific Ranges
Print selected rows or ranges:
const printSelectedRows = () => {
const treeGrid = treeGridRef.current;
const selectedIndexes = treeGrid.getSelectedRowIndexes();
// Print only selected rows
treeGrid.print();
};
<TreeGridComponent ref={treeGridRef} dataSource={data} childMapping="Children">
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
</TreeGridComponent>Hierarchy Printing
Control printing of hierarchical levels:
<TreeGridComponent
dataSource={data}
childMapping="Children"
printMode="AllPages" // 'AllPages', 'CurrentPage', 'ExternalExport'
>
<ColumnsDirective>
<ColumnDirective field="TaskName" headerText="Task Name" width={160} />
</ColumnsDirective>
</TreeGridComponent>Key APIs
| Property/Method | Type | Description |
|---|---|---|
print | method | Trigger print dialog |
printMode | string | 'AllPages', 'CurrentPage', 'ExternalExport' |
toolbar | array | Add print button to toolbar |
Common Patterns
1. Print Preview: Preview before printing 2. Sheet Orientation: Set portrait/landscape per page 3. Include Totals: Print with aggregate row
Related skills
How it compares
Pick syncfusion-react-treegrid for Syncfusion-licensed hierarchical grids; use generic React table skills when Syncfusion is not in the stack.
FAQ
What UI problem does syncfusion-react-treegrid solve?
syncfusion-react-treegrid helps React developers implement Syncfusion TreeGrid for hierarchical tabular data with expand-collapse rows, column templates, and editing. The skill targets enterprise admin dashboards where nested relationships must render without custom tree-table co
When should developers invoke syncfusion-react-treegrid?
Developers should invoke syncfusion-react-treegrid when building React admin surfaces that need Syncfusion TreeGrid for parent-child data such as org charts, file trees, or category hierarchies with sorting and inline editing support.