Expand and Collapse Nodes in React Diagram

21 Aug 202624 minutes to read

The React Diagram component provides built-in support for expanding and collapsing nodes, enabling users to create hierarchical views where child nodes can be hidden or shown dynamically. This functionality is particularly useful for organizational charts, mind maps, and tree structures where managing visual complexity is essential.

The expand and collapse feature allows users to:

  • Compress hierarchical views to show only root elements.
  • Toggle visibility of child nodes interactively.
  • Customize the appearance of expand and collapse icons.
  • Control the initial state of nodes programmatically.

The following properties control the expand and collapse behavior of nodes:

  • expandIcon - Defines the icon displayed when a node can be expanded.
  • collapseIcon - Defines the icon displayed when a node can be collapsed.

NOTE

Icons are only created when the node has outgoing edges (outEdges).

For detailed API information, refer to expandIcon and collapseIcon.

Customizing expand and collapse icons

Size and shape configuration

Both expandIcon and collapseIcon must be defined on a node before its icons can be customized.

Define the size of icons using the width and height properties.

The shape property of expandIcon and collapseIcon allows customization of the icon appearance. The following shape values are supported:

  • None
  • ArrowDown
  • ArrowUp
  • ArrowLeft
  • ArrowRight
  • Minus
  • Plus
  • Template
  • Path

The following code example demonstrates how to create icons with various shapes:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import {
  DiagramComponent,
  Inject,
  HierarchicalTree,
  DataBinding,
} from '@syncfusion/ej2-react-diagrams';
import { DataManager, Query } from '@syncfusion/ej2-data';
//Initializes data source
let data = [
  {
    Name: 'Node 1',
  },
  {
    Name: 'Node 2',
    ReportingPerson: 'Node 1',
  },
];
let items = new DataManager(data, new Query().take(7));
function App() {
  return (
    <div className="control-pane">
      <div className="control-section">
        <div className="content-wrapper" style=>
          <DiagramComponent
            id="container"
            width={'100%'}
            height={'550px'}
            //Uses layout to auto-arrange nodes on the diagram page
            layout=
            //Configures data source for diagram
            dataSourceSettings=
            //Sets the default properties for nodes
            getNodeDefaults={(obj) => {
              obj.shape = {
                type: 'Text',
                content: obj.data.Name,
              };
              obj.style = {
                fill: 'None',
                strokeColor: 'none',
                strokeWidth: 2,
                bold: true,
                color: 'white',
              };
              obj.borderColor = 'white';
              obj.width = 100;
              obj.height = 40;
              obj.backgroundColor = '#6BA5D7';
              obj.borderWidth = 1;
              obj.shape.margin = {
                left: 5,
                right: 5,
                top: 5,
                bottom: 5,
              };
              obj.expandIcon = {
                height: 10,
                width: 10,
                shape: 'ArrowDown',
                fill: 'lightgray',
                offset: { x: 0.5, y: 1 },
              };
              obj.collapseIcon.offset = { x: 0.5, y: 1 };
              obj.collapseIcon.height = 10;
              obj.collapseIcon.width = 10;
              obj.collapseIcon.shape = 'ArrowUp';
              obj.collapseIcon.fill = 'lightgray';
              return obj;
            }}
          >
            <Inject services={[DataBinding, HierarchicalTree]} />
          </DiagramComponent>
        </div>
      </div>
    </div>
  );
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from 'react-dom';
import {
  DiagramComponent,
  Inject,
  HierarchicalTree,
  DataBinding,
} from "@syncfusion/ej2-react-diagrams";
import { DataManager, Query } from "@syncfusion/ej2-data";
//Initializes data source
let data: object[] = [
  {
    Name: 'Node 1',
  },
  {
    Name: 'Node 2',
    ReportingPerson: 'Node 1',
  },
];
let items = new DataManager(data, new Query().take(7));
function App() {
  return (
    <div className="control-pane">
      <div className="control-section">
        <div className="content-wrapper" style=>
          <DiagramComponent
            id="container"
            width={'100%'}
            height={'550px'}
            //Uses layout to auto-arrange nodes on the diagram page
            layout=
            //Configures data source for diagram
            dataSourceSettings=
            //Sets the default properties for nodes
            getNodeDefaults={(obj) => {
              obj.shape = {
                type: 'Text',
                content: obj.data.Name,
              };
              obj.style = {
                fill: 'None',
                strokeColor: 'none',
                strokeWidth: 2,
                bold: true,
                color: 'white',
              };
              obj.borderColor = 'white';
              obj.width = 100;
              obj.height = 40;
              obj.backgroundColor = '#6BA5D7';
              obj.borderWidth = 1;
              obj.shape.margin = {
                left: 5,
                right: 5,
                top: 5,
                bottom: 5,
              };
              obj.expandIcon = {
                height: 10,
                width: 10,
                shape: 'ArrowDown',
                fill: 'lightgray',
                offset: { x: 0.5, y: 1 },
              };
              obj.collapseIcon.offset = { x: 0.5, y: 1 };
              obj.collapseIcon.height = 10;
              obj.collapseIcon.width = 10;
              obj.collapseIcon.shape = 'ArrowUp';
              obj.collapseIcon.fill = 'lightgray';
              return obj;
            }}
          >
            <Inject services={[DataBinding, HierarchicalTree]} />
          </DiagramComponent>
        </div>
      </div>
    </div>
  );
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);

Styling and appearance

Customize the visual appearance of icons using the borderColor, borderWidth, and fill properties.

The corner radius can be set using the cornerRadius property of the icon.

Icons can be precisely positioned relative to node boundaries using margin, offset, horizontalAlignment, and verticalAlignment settings. While combining all four alignment properties provides maximum control, it requires careful consideration of their interactions.

For example, to align an icon to the right with a small margin:

expandIcon: { shape: 'ArrowDown', width: 20, height: 15, horizontalAlignment: 'Right', margin: { left: 5 } }

The iconColor property can be used to set the icon’s stroke/foreground color.

The following code example illustrates the customization of icons.

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import {
  DiagramComponent,
  Inject,
  HierarchicalTree,
  DataBinding,
} from '@syncfusion/ej2-react-diagrams';
import { DataManager, Query } from '@syncfusion/ej2-data';
//Initializes data source
let data = [
  {
    Name: 'Node 1',
  },
  {
    Name: 'Node 2',
    ReportingPerson: 'Node 1',
  },
];
let items = new DataManager(data, new Query().take(7));
function App() {
  return (
    <div className="control-pane">
      <div className="control-section">
        <div className="content-wrapper" style=>
          <DiagramComponent
            id="container"
            width={'100%'}
            height={'550px'}
            //Uses layout to auto-arrange nodes on the diagram page
            layout=
            //Configures data source for diagram
            dataSourceSettings=
            //Sets the default properties for nodes
            getNodeDefaults={(obj) => {
              obj.shape = {
                type: 'Text',
                content: obj.data.Name,
              };
              obj.style = {
                fill: 'None',
                strokeColor: 'none',
                strokeWidth: 2,
                bold: true,
                color: 'white',
              };
              obj.borderColor = 'white';
              obj.width = 100;
              obj.height = 40;
              obj.backgroundColor = '#6BA5D7';
              obj.borderWidth = 1;
              obj.shape.margin = {
                left: 5,
                right: 5,
                top: 5,
                bottom: 5,
              };
              obj.expandIcon = {
                height: 10,
                width: 10,
                shape: 'ArrowDown',
                fill: 'lightgray',
                offset: { x: 0.5, y: 1 },
              };
              obj.collapseIcon.offset = { x: 0.5, y: 1 };
              obj.collapseIcon.height = 10;
              obj.collapseIcon.width = 10;
              obj.collapseIcon.shape = 'ArrowUp';
              obj.collapseIcon.fill = 'lightgray';
              return obj;
            }}
          >
            <Inject services={[DataBinding, HierarchicalTree]} />
          </DiagramComponent>
        </div>
      </div>
    </div>
  );
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from 'react-dom';
import {
  DiagramComponent,
  Inject,
  HierarchicalTree,
  DataBinding,
} from "@syncfusion/ej2-react-diagrams";
import { DataManager, Query } from "@syncfusion/ej2-data";
//Initializes data source
let data: object[] = [
  {
    Name: 'Node 1',
  },
  {
    Name: 'Node 2',
    ReportingPerson: 'Node 1',
  },
];
let items = new DataManager(data, new Query().take(7));
function App() {
  return (
    <div className="control-pane">
      <div className="control-section">
        <div className="content-wrapper" style=>
          <DiagramComponent
            id="container"
            width={'100%'}
            height={'550px'}
            //Uses layout to auto-arrange nodes on the diagram page
            layout=
            //Configures data source for diagram
            dataSourceSettings=
            //Sets the default properties for nodes
            getNodeDefaults={(obj) => {
              obj.shape = {
                type: 'Text',
                content: obj.data.Name,
              };
              obj.style = {
                fill: 'None',
                strokeColor: 'none',
                strokeWidth: 2,
                bold: true,
                color: 'white',
              };
              obj.borderColor = 'white';
              obj.width = 100;
              obj.height = 40;
              obj.backgroundColor = '#6BA5D7';
              obj.borderWidth = 1;
              obj.shape.margin = {
                left: 5,
                right: 5,
                top: 5,
                bottom: 5,
              };
              obj.expandIcon = {
                height: 10,
                width: 10,
                shape: 'ArrowDown',
                fill: 'lightgray',
                offset: { x: 0.5, y: 1 },
              };
              obj.collapseIcon.offset = { x: 0.5, y: 1 };
              obj.collapseIcon.height = 10;
              obj.collapseIcon.width = 10;
              obj.collapseIcon.shape = 'ArrowUp';
              obj.collapseIcon.fill = 'lightgray';
              return obj;
            }}
          >
            <Inject services={[DataBinding, HierarchicalTree]} />
          </DiagramComponent>
        </div>
      </div>
    </div>
  );
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);

Managing node expansion state

The isExpanded property controls whether a node displays its child nodes. Default value: true. When set to true, child nodes are visible; when false, they are hidden.

The following example demonstrates how to configure the expansion state of nodes:

import { NodeModel } from '@syncfusion/ej2-react-diagrams';

let node: NodeModel = {
        id: 'Start', width: 140, height: 50, offsetX: 300, offsetY: 50,
        //Expand state of node
        isExpanded: false,
        expandIcon: {shape: 'ArrowDown',   width: 20,
        height: 15},
        collapseIcon: {shape: 'ArrowUp',  width: 20,
        height: 15}
    }