Creating a file/folder structure in React.

In this Blog I am just breaking down my approach of building a file and folder structure

User Avatar

Faisal Husain

Creating a file/folder structure in React. blog image

Ok lets start breaking down the problem

I will approach this problem in 4 steps:

  1. Create a data file - This mean in most of the cases I have to prepare this file so that I cant display file or folder on screen
  2. Displaying file/folder - At this step we will tackle how to display date in tree format on screen.
  3. Expanding/Collapsible Functionality - Expand or Collapse Folder as file cannot be expanded 😅
  4. Adding File/Folder - Adding file or folder to tree.
  5. Deleting File/Folder - Deleting file or folder from tree.

Step 1 - Create a data file.

So just think how show a data file look like. It will be array of objects and each object will have an id, name , boolean of isFolder and childrens (if its a folder)

data.js
export const data = [
  {
    id: "1",
    isFolder: true,
    name: "public",
    children: [
      {
        id: "2",
        isFolder: false,
        name: "index.html",
      },
    ],
  },
  {
    id: "3",
    isFolder: true,
    name: "src",
    children: [
      {
        id: "4",
        isFolder: true,
        name: "components",
        children: [
          {
            id: "5",
            isFolder: false,
            name: "FileExplorer.tsx",
          },
        ],
      },
      {
        id: "6",
        isFolder: true,
        name: "data",
        children: [
          {
            id: "7",
            isFolder: false,
            name: "data.ts",
          },
        ],
      },
      {
        id: "8",
        isFolder: true,
        name: "pages",
        children: [
          {
            id: "9",
            isFolder: false,
            name: "Home.tsx",
          },
        ],
      },
      {
        id: "10",
        isFolder: false,
        name: "App.tsx",
      },
      {
        id: "11",
        isFolder: false,
        name: "index.tsx",
      },
      {
        id: "12",
        isFolder: false,
        name: "styles.css",
      },
    ],
  },
  {
    id: "13",
    isFolder: false,
    name: "package.json",
  },
  {
    id: "14",
    isFolder: false,
    name: "tsconfig.json",
  },
];    

Try to create this on your own as this take time.

Step 2 - Displaying file/folder.

📁 public
📄 index.html
📁 src
📁 components
📄 FileExplorer.tsx
📁 data
📄 data.ts
📁 pages
📄 Home.tsx
📄 App.tsx
📄 index.tsx
📄 styles.css
📄 package.json
📄 tsconfig.json
file-folder.tsx
"use client"
import { useState } from "react";
import { data, FileNode } from "./data";
 
const FolderStructure = ({ data }: { data: FileNode[] }) => {
    return (
        <div>
            {data.map((node: FileNode) => (
                <div key={node.id} className="pl-4 cursor-pointer">
                   {node.isFolder ? "📁" :"📄"} {node.name}
                    {node.children && <FolderStructure data={node.children} />}
                </div>
            ))}
        </div>
    )
}
const Ff = () => {
 
    const [initialData, setInitialData] = useState(data)
 
    return (
        <div className="w-[calc(100vw-2rem)] md:w-[700px] bg-black p-3 ">
            <div className="flex items-center justify-center">
                <FolderStructure data={initialData} />
            </div>
        </div>
    );
};
 
export default Ff;

Step 3 - Expanding/Collapsible Functionality.

📁 public
📁 src
📄 package.json
📄 tsconfig.json
file-folder.tsx
"use client"
import { useState } from "react";
import { data, FileNode } from "./data";
 
const FolderStructure = ({ data }: { data: FileNode[] }) => {
    const [expanded, setExpanded] = useState<{ [key: string]: boolean }>({})
    return (
        <div>
            {data.map((node: FileNode) => (
                <div key={node.id} className="pl-4 cursor-pointer">
                    <div onClick={()=>{
                        setExpanded((prev)=>({
                            ...prev,
                            [node.id]: !prev[node.id] 
                        }))
                    }}>
                        {node.isFolder ? (expanded?.[node.id] ? "📂" :"📁") : "📄"} {node.name}
                    </div>
 
                    { expanded?.[node.id] && node.children && <FolderStructure data={node.children} />}
                </div>
            ))}
        </div>
    )
}
const Ff = () => {
 
    const [initialData, setInitialData] = useState(data)
 
    return (
        <div className="w-[calc(100vw-2rem)] md:w-[700px] bg-black p-3 border-zinc-800 border-2 rounded-2xl ">
            <div className="flex items-center justify-center ">
                <FolderStructure data={initialData} />
            </div>
        </div>
    );
};
 
export default Ff;

Step 4 - Adding File/Folder.

📁public
📁src
📄package.json
📄tsconfig.json
file-folder.tsx
"use client"
import { Children, useState } from "react";
import { data, FileNode } from "./data";
import { Button } from "@/components/ui/button";
 
 
 
const FolderStructure = ({ data, addFolder }: { data: FileNode[], addFolder: (parentId: any) => void }) => {
    const [expanded, setExpanded] = useState<{ [key: string]: boolean }>({})
    return (
        <div>
            {data.map((node: FileNode) => (
                <div key={node.id} className="pl-4 cursor-pointer">
                    <div >
                        <span onClick={(e) => {
                            setExpanded((prev) => ({
                                ...prev,
                                [node.id]: !prev[node.id]
                            }))
                        }}>
                            {node.isFolder ? (expanded?.[node.id] ? "📂" : "📁") : "📄"}
                        </span>
                        <span>
                            {node.name}
                        </span>
                        <Button onClick={(e) => {
                            e.preventDefault()
                            addFolder(node.id)
                        }}>

                        </Button>
                    </div>
 
                    {expanded?.[node.id] && node.children && <FolderStructure data={node.children} addFolder={addFolder} />}
                </div>
            ))}
        </div>
    )
}
const Ffs = () => {
 
    const [initialData, setInitialData] = useState(data)
 
    const addFolder = (parentId: any) => {
        const updateTree = (data: any) => {
            return data.map((node: FileNode) => {
                if (node.id === parentId) {
                    return {
                        ...node,
                        children: [
                            ...(node.children || []),
                            {
                                id: Date.now().toString(),
                                name: "New Folder",
                                isFolder: true,
                                children: []
                            }
                        ]
                    }
                }
                if (node.children) {
                    return {
                        ...node,
                        children: updateTree(node.children)
                    }
                }
                return node
            })
        }
        setInitialData((pre) => updateTree(pre))
    }
 
    return (
        <div className="w-[calc(100vw-2rem)] md:w-[700px] bg-black p-3 border-zinc-800 border-2 rounded-2xl ">
            <div className="flex items-center justify-center ">
                <FolderStructure data={initialData} addFolder={addFolder} />
            </div>
        </div>
    );
};
 
export default Ffs;

Step 5 - Deleting File/Folder

📁public
📁src
📄package.json
📄tsconfig.json
file-folder.tsx
"use client"
import { Children, useState } from "react";
import { data, FileNode } from "./data";
import { Button } from "@/components/ui/button";
 
 
 
const FolderStructure = ({ data, addFolder, deleteFileorFolder }: { data: FileNode[], addFolder: (parentId: any) => void, deleteFileorFolder: (parentId: any) => void }) => {
    const [expanded, setExpanded] = useState<{ [key: string]: boolean }>({})
    return (
        <div>
            {data.map((node: FileNode) => (
                <div key={node.id} className="pl-4 cursor-pointer">
                    <div >
                        <span onClick={(e) => {
                            setExpanded((prev) => ({
                                ...prev,
                                [node.id]: !prev[node.id]
                            }))
                        }}>
                            {node.isFolder ? (expanded?.[node.id] ? "📂" : "📁") : "📄"}
                        </span>
                        <span>
                            {node.name}
                        </span>
                        <Button onClick={(e) => {
                            e.preventDefault()
                            addFolder(node.id)
                        }}>

                        </Button>
                        <Button onClick={(e) => {
                            e.preventDefault()
                            deleteFileorFolder(node.id)
                        }}>
                            🗑️
                        </Button>
                    </div>
 
                    {expanded?.[node.id] && node.children && <FolderStructure data={node.children} addFolder={addFolder} deleteFileorFolder={deleteFileorFolder} />}
                </div>
            ))}
        </div>
    )
}
const Ffs = () => {
 
    const [initialData, setInitialData] = useState(data)
 
    const addFolder = (parentId: any) => {
        const updateTree = (data: any) => {
            return data.map((node: FileNode) => {
                if (node.id === parentId) {
                    return {
                        ...node,
                        children: [
                            ...(node.children || []),
                            {
                                id: Date.now().toString(),
                                name: "New Folder",
                                isFolder: true,
                                children: []
                            }
                        ]
                    }
                }
                if (node.children) {
                    return {
                        ...node,
                        children: updateTree(node.children)
                    }
                }
                return node
            })
        }
        setInitialData((pre) => updateTree(pre))
    }
 
    const deleteFileorFolder = (parentId: any) => {
        const updateTree = (data: any) => {
            return data.map((node: FileNode) => {
                if (node.id === parentId) {
                    return null
                }
                if (node.children) {
                    return {
                        ...node,
                        children: updateTree(node.children)
                    }
                }
                return node
            }).filter(Boolean);
        }
        setInitialData((pre) => updateTree(pre))
    }
 
    return (
        <div className="w-[calc(100vw-2rem)] md:w-[700px] bg-black p-3 border-zinc-800 border-2 rounded-2xl ">
            <div className="flex items-center justify-center ">
                <FolderStructure data={initialData} addFolder={addFolder} deleteFileorFolder={deleteFileorFolder} />
            </div>
        </div>
    );
};
 
export default Ffs;
 

Here is new Trick I learned if you filter(Boolen) it will remove all the null/undefined value.

All Step done.

Want to hire me as a freelancer? Let's discuss.
Drop a message and let's discuss
Drop in your email ID and I will get back to you.
HomeAboutProjectsBlogsHire MeCrafts