This solution assumes that you use unique id for each item in the tree.

It uses Depth first search algorithm.

Before trying please fix your sample data non-unique IDs. I didn't notice them at first and wasted time debugging for no bug.

function dfs(node, term, foundIDS) {
  // Implement your search functionality
  let isMatching = node.name && node.name.indexOf(term) > -1;

  if (Array.isArray(node.children)) {
    node.children.forEach((child) => {
      const hasMatchingChild = dfs(child, term, foundIDS);
      isMatching = isMatching || hasMatchingChild;
    });
  }

  // We will add any item if it matches our search term or if it has a children that matches our term
  if (isMatching && node.id) {
    foundIDS.push(node.id);
  }

  return isMatching;
}

function filter(data, matchedIDS) {
  return data
    .filter((item) => matchedIDS.indexOf(item.id) > -1)
    .map((item) => ({
      ...item,
      children: item.children ? filter(item.children, matchedIDS) : [],
    }));
}

function search(term) {
  // We wrap data in an object to match the node shape
  const dataNode = {
    children: data,
  };

  const matchedIDS = [];
  // find all items IDs that matches our search (or their children does)
  dfs(dataNode, term, matchedIDS);

  // filter the original data so that only matching items (and their fathers if they have) are returned
  return filter(data, matchedIDS);
}
Answer from rksh1997 on Stack Overflow
🌐
MUI
mui.com › x › react-tree-view
Tree View React component - MUI X
The Tree View components let users navigate hierarchical lists of data with nested levels that can be expanded and collapsed.
🌐
Material-UI
v4.mui.com › components › tree-view
Tree View React component - Material-UI [v4]
While the TreeView/TreeItem component API maximizes flexibility, an extra step is needed to handle a rich object.
🌐
GitHub
github.com › fiffty › react-treeview-mui
GitHub - fiffty/react-treeview-mui: A Treeview UI written as a React Component (with material design)
Receives the search term (String) as first parameter. ... Only for MuiTreeList. If true, will use folder and file icons for list items. The Component uses ReactCSSTransitionGroup to animate the items inside the treeview.
Starred by 164 users
Forked by 50 users
Languages   JavaScript 100.0% | JavaScript 100.0%
🌐
GitHub
github.com › helfi92 › material-ui-treeview
GitHub - helfi92/material-ui-treeview: A React tree view for material-ui. · GitHub
A React tree view for material-ui with TypeScript support.
Starred by 47 users
Forked by 22 users
Languages   JavaScript 88.7% | CSS 8.7% | Shell 2.6%
🌐
npm
npmjs.com › package › material-ui-treeview
material-ui-treeview - npm
April 29, 2020 - A React tree view for material-ui with TypeScript support.
      » npm install material-ui-treeview
    
Published   Apr 29, 2020
Version   5.0.0
Author   Hassan Ali
🌐
Stack Overflow
stackoverflow.com › questions › 72959594 › material-ui-treeview-filter
Material UI TreeView Filter
const treeViewApiData = { name: "fl1", path: "folder 1", children: [ { name: "f1", path: "file 1", children: [], isFile: true, }, { name: "f2", path: "file 2", children: [], isFile: true, }, { name: "f3", path: "file 3", children: [], isFile: true, }, ], isFile: false, };
Top answer
1 of 4
10

I needed a to deal with tree data in a project as well. I ended up creating MUI Tree Select.

You can demo it in this sandbox.

2 of 4
2

I searched a lot for that in the end I made this by myself sandbox. you can choose to parent or child with this and you can custom it easily.

    import { ThemeProvider, createTheme } from "@mui/material/styles";
import React, { useState } from "react";
import ReactDOM from "react-dom";
import TreeItem from "@mui/lab/TreeItem";
import { Popover, TextField, Typography } from "@mui/material";
import clsx from "clsx";
import { TreeView, useTreeItem } from "@mui/lab";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import { useMediaQuery } from "@mui/material";
const data = [
  {
    id: "root",
    name: "Parent",
    children: [
      {
        id: "1",
        name: "Child - 1"
      },
      {
        id: "3",
        name: "Child - 3",
        children: [
          {
            id: "4",
            name: "Child - 4"
          }
        ]
      }
    ]
  },
  {
    id: "1root",
    name: "Parent1",
    children: [
      {
        id: "5",
        name: "Child - 1-1"
      },
      {
        id: "7",
        name: "Child - 3-1",
        children: [
          {
            id: "8",
            name: "Child - 4-1"
          }
        ]
      }
    ]
  }
];
const CustomContent = React.forwardRef(function CustomContent(props, ref) {
  const {
    classes,
    className,
    label,
    nodeId,
    icon: iconProp,
    expansionIcon,
    displayIcon
  } = props;

  const {
    disabled,
    expanded,
    selected,
    focused,
    handleExpansion,
    handleSelection,
    preventSelection
  } = useTreeItem(nodeId);

  const icon = iconProp || expansionIcon || displayIcon;

  const handleMouseDown = (event) => {
    preventSelection(event);
  };

  const handleExpansionClick = (event) => {
    handleExpansion(event);
  };

  const handleSelectionClick = (event) => {
    handleSelection(event);
  };

  return (
    // eslint-disable-next-line jsx-a11y/no-static-element-interactions
    <div
      className={clsx(className, classes.root, {
        [classes.expanded]: expanded,
        [classes.selected]: selected,
        [classes.focused]: focused,
        [classes.disabled]: disabled
      })}
      onMouseDown={handleMouseDown}
      ref={ref}
      style={{ padding: "3px 0" }}
    >
      <div onClick={handleExpansionClick} className={classes.iconContainer}>
        {icon}
      </div>
      <Typography
        onClick={handleSelectionClick}
        component="div"
        className={classes.label}
      >
        {label}
      </Typography>
    </div>
  );
});
const CustomTreeItem = (props) => (
  <TreeItem ContentComponent={CustomContent} {...props} />
);

export default function RichObjectTreeView({ formik, edit }) {
  const [anchorEl, setAnchorEl] = React.useState(null);
  const [equipmentItem, setEquipmentItem] = useState("");
  const [equipmentId, setEquipmentId] = useState("");
  const handleClick = (event) => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  const open = Boolean(anchorEl);
  const id = open ? "simple-popover" : undefined;

  const renderTree = (nodes) => (
    <CustomTreeItem key={nodes.id} nodeId={nodes.id} label={nodes.name}>
      {Array.isArray(nodes.children)
        ? nodes.children.map((node) => renderTree(node))
        : null}
    </CustomTreeItem>
  );

  return (
    <>
      <TextField
        variant="standard"
        required={false}
        label="Equipment Item"
        name="equipmentItem"
        id="equipmentItem"
        defaultValue={equipmentItem}
        value={equipmentItem}
        className="w-100"
        inputProps={{ readOnly: !edit }}
        onClick={handleClick}
      />

      <Popover
        id={id}
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "left"
        }}
      >
        <TreeView
          aria-label="icon expansion"
          defaultSelected={equipmentId}
          selected={equipmentId}
          defaultCollapseIcon={<ExpandMoreIcon />}
          defaultExpandIcon={<ChevronRightIcon />}
          onNodeSelect={(e, id) => {
            setEquipmentId(id);
            setEquipmentItem(e.target.innerText);
          }}
          sx={{
            height: 200,
            flexGrow: 1,
            minWidth: "200px",
            overflowY: "auto"
          }}
        >
          {data.map((item, i) => renderTree(item))}
        </TreeView>
      </Popover>
    </>
  );
}
Find elsewhere
🌐
CodeSandbox
codesandbox.io › s › wkp03p2jrl
material-ui-tree demo - CodeSandbox
January 29, 2020 - A demo of material-ui-tree.
Published   Apr 08, 2018
Author   shallinta
🌐
Material UI
v4.mui.com › api › tree-view
TreeView API - Material-UI [v4]
The API documentation of the TreeView React component. Learn more about the props and the CSS customization points.
🌐
Whizsid
whizsid.github.io › blog › 10 › displaying-a-list-of-paths-as-a-material-ui-tree-view.html
WhizSid| Displaying a list of paths as a Material UI tree view
Material UI is a popular frontend react framework. And it has very useful and easy to use components. But when you developing a react tree viewer for browsing files, it will mess you up.
🌐
CodeSandbox
codesandbox.io › examples › package › material-ui-treeview
material-ui-treeview examples - CodeSandbox
Use this online material-ui-treeview playground to view and fork material-ui-treeview example apps and templates on CodeSandbox.
🌐
DevExtreme
js.devexpress.com › Demos › WidgetsGallery › Demo › TreeView › TreeViewWithSearchBar › React › Light
TreeView with Search Bar - DevExtreme Tree View: React Components by DevExpress
Configure the searchExpr property to specify custom search fields. The default search field is text. The TreeView uses the TextBox component as a search bar.
🌐
Medium
mohammad-hassan-fallah.medium.com › react-tree-component-with-filter-f1d052c5d11d
React Tree Component with Filter. One of the most important ways to… | by Mohammad Hassan Fallah | Medium
January 18, 2021 - In this post, we are supposed to teach a filtered tree-component with the help of React and Material-UI. Now, we will explain the implementation levels step by step. First, create a React project with the following command. ... Now, according to this link, you can create a TreeView component, which TreeItem is created based on children and recursive function.
🌐
johnnyreilly
johnnyreilly.com › home › blog › mui react tree view: check children, uncheck parents
MUI React Tree View: check children, uncheck parents | johnnyreilly
May 25, 2024 - This is the behaviour that I'm used to in a treeview component. In action it looks like this: We can implement this behaviour by tracking the selected nodes and then determining which nodes should be selected based on the current selection. The code below demonstrates how to do this: import * as React from 'react'; import Box from '@mui/material/Box'; import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; import { TreeViewBaseItem } from '@mui/x-tree-view/models'; const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ { id: 'grid', label: 'Data Grid', children: [ { id: 'grid-community', label: '@
Top answer
1 of 2
3

For anyone still looking for a solution to this problem I've recently tackled it using a combination of the selected and expanded props in the TreeView API. See this Code Sandbox demo for an example of how to asynchronously load new children and expand their parent once they are loaded.

import React from "react";
import TreeView from "@material-ui/lab/TreeView";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import ChevronRightIcon from "@material-ui/icons/ChevronRight";
import TreeItem from "@material-ui/lab/TreeItem";
import TreeNode from "./TreeNode";

const mockApiCall = async () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      const nextId = Math.ceil(Math.random() * 100);
      resolve([
        {
          id: `${nextId}`,
          name: `child-${nextId}`,
          children: []
        },
        {
          id: `${nextId + 1}`,
          name: `child-${nextId + 1}`,
          children: []
        }
      ]);
    }, Math.ceil(Math.random() * 1000));
  });
};

export default class Demo extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      expanded: [],
      selected: "1",
      tree: new TreeNode({
        id: "1",
        name: "src",
        children: []
      })
    };
  }

  handleChange = async (event, nodeId) => {
    const node = this.state.tree.search(nodeId);
    if (node && !node.children.length) {
      mockApiCall()
        .then((result) => {
          this.setState({ tree: this.state.tree.addChildren(result, nodeId) });
        })
        .catch((err) => console.error(err))
        .finally(() => {
          this.setState({
            selected: nodeId,
            expanded: [...this.state.expanded, nodeId]
          });
        });
    }
  };

  createItemsFromTree = (tree) => {
    if (tree.children.length) {
      return (
        <TreeItem key={tree.id} nodeId={tree.id} label={tree.name}>
          {tree.children.length > 0 &&
            tree.children.map((child) => this.createItemsFromTree(child))}
        </TreeItem>
      );
    }
    return <TreeItem key={tree.id} nodeId={tree.id} label={tree.name} />;
  };

  render() {
    return (
      <TreeView
        defaultCollapseIcon={<ExpandMoreIcon />}
        defaultExpandIcon={<ChevronRightIcon />}
        selected={this.state.selected}
        onNodeSelect={this.handleChange}
        expanded={this.state.expanded}
      >
        {this.createItemsFromTree(this.state.tree)}
      </TreeView>
    );
  }
}
2 of 2
0
<div class="container-fluid">
    <div class="row mt-3">
        <div class="col-lg-8">
            <div class="card">
                <div class="card-body">
                    <div id="tree">
                        <div class="branch">
                            <div class="entry main-entry"><span class="drop"><p-dropdown [options]="logicGates"
                                        optionLabel="name" placeholder="Select" /></span>
                                <div class="branch">
                                    <div class="entry">
                                        <span class="drop">
                                            <div class="card sub-card p-2">
                                                <div class="row align-items-center">
                                                    <div class="col-lg-4">
                                                        <select name="" id="" class="form-select">
                                                            <option value="">Select Field</option>
                                                        </select>
                                                    </div>
                                                    <div class="col-lg-4">
                                                        <select name="" id="" class="form-select">
                                                            <option value="">Select Condition</option>
                                                        </select>
                                                    </div>
                                                    <div class="col-lg-3">
                                                        <input type="text" class="form-control"
                                                            placeholder="Field Text">
                                                    </div>
                                                    <div class="col-lg-1">
                                                        <button class="btn border-0"><i
                                                                class='bx bx-trash'></i></button>
                                                    </div>
                                                </div>
                                            </div>
                                        </span>
                                    </div>
                                    <div class="entry">
                                        <span class="drop">
                                            <div class="card sub-card p-2">
                                                <div class="row align-items-center">
                                                    <div class="col-lg-4">
                                                        <select name="" id="" class="form-select">
                                                            <option value="">Select Field</option>
                                                        </select>
                                                    </div>
                                                    <div class="col-lg-4">
                                                        <select name="" id="" class="form-select">
                                                            <option value="">Select Condition</option>
                                                        </select>
                                                    </div>
                                                    <div class="col-lg-3">
                                                        <input type="text" class="form-control"
                                                            placeholder="Field Text">
                                                    </div>
                                                    <div class="col-lg-1">
                                                        <button class="btn border-0"><i
                                                                class='bx bx-trash'></i></button>
                                                    </div>
                                                </div>
                                            </div>
                                        </span>
                                    </div>
                                </div>
                                <div class="margin">
                                    <a href="">+ Add Condition</a>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="card-footer">
                    <button class="btn border-0 btn_foot me-3">
                        <i class='bx bx-plus-circle'></i> Add Rule
                    </button>
                    <button class="btn border-0 btn_foot">
                        <i class='bx bx-folder-plus'></i> Add Inner Group
                    </button>
                </div>
            </div>
        </div>
    </div>
</div>

#tree {
  display: inline-block;
  padding: 10px;
  width: 100%;
}

#tree * {
  box-sizing: border-box;
}

#tree .branch {
  padding: 20px 0 5px 20px;
}

#tree .branch:not(:first-child) {
  margin-left: 170px;
}

#tree .branch:not(:first-child):after {
  content: "";
  width: 20px;
  border-top: 1px solid #ccc;
  position: absolute;
  left: 150px;
  top: 50%;
  margin-top: 1px;
}

.entry {
  position: relative;
  min-height: 100px;
  display: block;
}

.entry:before {
  content: "";
  height: 100%;
  border-left: 1px solid #ccc;
  position: absolute;
  left: -20px;
}

.entry:first-child:after {
  height: 10px;
  border-radius: 10px 0 0 0;
}

.entry:first-child:before {
  width: 10px;
  height: 50%;
  top: 50%;
  margin-top: 1px;
  border-radius: 10px 0 0 0;
}

.entry:after {
  content: "";
  width: 20px;
  transition: border 0.5s;
  border-top: 1px solid #ccc;
  position: absolute;
  left: -20px;
  top: 50%;
  margin-top: 1px;
}

.entry:last-child:before {
  width: 10px;
  height: 50%;
  border-radius: 0 0 0 10px;
}
.entry:last-child:after {
  height: 10px;
  border-top: none;
  transition: border 0.5s;
  border-bottom: 1px solid #ccc;
  border-radius: 0 0 0 10px;
  margin-top: -9px;
}

.entry:only-child:after {
  width: 10px;
  height: 0px;
  margin-top: 1px;
  border-radius: 0px;
}

.entry:only-child:before {
  display: none;
}

.entry span {
  border: 1px solid #ccc;
  display: block;
  min-width: 150px;
  padding: 5px 10px;
  line-height: 20px;
  text-align: center;
  position: absolute;
  left: 0;
  top: 50%;
  margin-top: -15px;
  color: #666;
  font-family: arial, verdana, tahoma;
  font-size: 14px;
  display: inline-block;
  border-radius: 5px;
  transition: all 0.5s;
}

// #tree .entry span:hover,
// #tree .entry span:hover + .branch .entry span {
//   background: #e6e6e6;
//   color: #000;
//   border-color: #a6a6a6;
// }

#tree .entry span:hover + .branch .entry::after,
#tree .entry span:hover + .branch .entry::before,
#tree .entry span:hover + .branch::before,
#tree .entry span:hover + .branch .branch::before {
  border-color: #a6a6a6;
}
::ng-deep {
  .p-dropdown {
    border-radius: 20px;
    width: 150px;
  }
  .p-inputtext {
    padding: 0.35rem 0.45rem;
    font-size: 14px;
  }
  .p-icon {
    width: 0.5rem;
    height: 0.5rem;
  }
  .p-dropdown-panel .p-dropdown-items {
    padding-left: 0rem;
    margin-bottom: 0rem;
  }
  .p-dropdown-panel .p-dropdown-items .p-dropdown-item {
    padding: 0.55rem 0.75rem;
    text-align: start;
  }
}

div.main-entry {
  .drop {
    border: 0px;
    padding: 0px;
  }
}

.margin {
  margin-left: 180px;
  a {
    text-decoration: none;
  }
}

button.btn_foot {
  background: #0367a51f;
  color: #0367a5;
}
Top answer
1 of 2
5

Material-UI 4.9.13 adds onIconClick props to the TreeItem. Since every TreeItem needs to attach a click listener, you should write a wrapper component to reuse the code:

function MyTreeItem(props) {
  return (
    <TreeItem
      {...props}
      onIconClick={() => console.log('delete', props.nodeId)}
    />
  );
}

Unfortunately, this props was removed in Material-UI 5.0.0 alpha-12 in favor of ContentComponent. This API requires you to write significantly more code, in exchange of more flexible customization:

import Typography from "@material-ui/core/Typography";
import IconButton from "@material-ui/core/IconButton";
import DeleteIcon from "@material-ui/icons/Delete";
import TreeView from "@material-ui/lab/TreeView";
import TreeItem, { useTreeItem } from "@material-ui/lab/TreeItem";
import clsx from "clsx";

const CustomContent = React.forwardRef(function CustomContent(props, ref) {
  const {
    classes,
    label,
    nodeId,
    icon: iconProp,
    expansionIcon,
    displayIcon,
    onDelete
  } = props;

  const {
    disabled,
    expanded,
    selected,
    focused,
    handleExpansion,
    handleSelection,
    preventSelection
  } = useTreeItem(nodeId);

  const icon = iconProp || expansionIcon || displayIcon;
  const handleDelete = () => onDelete(nodeId);

  return (
    <div
      className={clsx(classes.root, {
        [classes.expanded]: expanded,
        [classes.selected]: selected,
        [classes.focused]: focused,
        [classes.disabled]: disabled
      })}
      onMouseDown={preventSelection}
      ref={ref}
    >
      <div onClick={handleExpansion} className={classes.iconContainer}>
        {icon}
      </div>
      <IconButton size="small" onClick={handleDelete}>
        <DeleteIcon fontSize="small" />
      </IconButton>
      <Typography
        onClick={handleSelection}
        component="div"
        className={classes.label}
      >
        {label}
      </Typography>
    </div>
  );
});

TreeItem

function MyTreeItem(props) {
  return (
    <TreeItem
      {...props}
      ContentComponent={CustomContent}
      ContentProps={{
        onDelete: (nodeId) => console.log("delete", nodeId)
      }}
    />
  );
}

TreeView

<TreeView
  defaultCollapseIcon={<ExpandMoreIcon />}
  defaultExpandIcon={<ChevronRightIcon />}
  multiSelect
>
  <MyTreeItem nodeId="1" label="Applications">
    <MyTreeItem nodeId="2" label="Calendar" />
    <MyTreeItem nodeId="3" label="Chrome" />
    <MyTreeItem nodeId="4" label="Webstorm" />
  </MyTreeItem>
  <MyTreeItem nodeId="5" label="Documents">
    <MyTreeItem nodeId="6" label="Material-UI">
      <MyTreeItem nodeId="7" label="src">
        <MyTreeItem nodeId="8" label="index.js" />
        <MyTreeItem nodeId="9" label="tree-view.js" />
      </MyTreeItem>
    </MyTreeItem>
  </MyTreeItem>
</TreeView>

Live Demo

2 of 2
0

In Material UI 5 (as of Feb 2024) using a controlled and styled TreeView with dynamic TreeItems, I've been going about it this way:

<TreeView 
   ...
   onNodeSelect={handleSelected}
   ...
/>

and handleSelected:

const handleSelected = (event) => {
   const itemId = event.target.parentNode.parentNode.id
   //
   // go do stuff with itemId
   //
}

I was bit surprised that I had to walk the DOM to find the actual TreeItem and suspect this is all subject to change. The MUI TreeView appears to be an extended work-in-progress or lost step child to the Material UI framework. I'm not sure which.