with content
file
works on a file too
Installation
npm i react-tree-folder # or bun, or pnpm
Simple Usage
import { TreeFolder, type Tree } from 'react-tree-folder'
// import this in your global layout
// if you use this on multiple pages
import 'react-tree-folder/dist/style.css'
const tree : Tree = [
{
text: 'Some Folder',
dir: true,
// a directory may be open by default
open: true,
// a directory may contain
// other directories or files
branch: [
{
text: 'Sub Folder',
dir: true,
},
{
text: 'Sub File',
}
]
},
{
text: 'Some File',
}
]
export function SimpleUsageExample(){
return <TreeFolder tree={tree} />
}
Simple Usage Result
Sub File
Some File
Types
export type Branch = {
text: string
open?: boolean
dir?: boolean
// custom icon
// for files
icon?: ReactNode
// custom icon
// for folder
folderIcon?: {
closed:ReactNode
open: ReactNode
}
// using onClick will render a button
onClick?: () => void
// using link will render an anchor
// using link and onClick,
// will also render an anchor
link?:{
href: string,
newTab?: boolean
}
// custom component
component?: (text: string) => ReactNode
branch?: Tree
// coloring works individualy
color?: string
hoverColor?: string
}
export type Tree = Branch[]
// without icons
// import { TreeFolder } from 'react-tree-folder/dist/no-icons'
export type ReactTreeFolderProps = {
tree: Tree
iconFolderOpen: ReactNode
iconFolder: ReactNode
iconFile?: ReactNode
// coloring works globaly
color?: string
hoverColor?: string
borderColor?: string
containerClass?: string
}
// with default icon
// import { TreeFolder } from 'react-tree-folder'
// this is the one we are using in the example above
export type ReactTreeWithIconsProps = {
tree: Tree
color?: string
// coloring works globaly
hoverColor?: string
borderColor?: string
containerClass?: string
}
Custom Icon
// Customize how the file/dir icons look like
const treeFolder:Tree = [
{
text: 'default icon'
},
{
text: 'unicode icon',
icon: '⚽'
},
{
text: 'react element as icon',
icon: <i style={{color:'orange', marginRight: '5px'}}>Hi</i>
},
{
text: 'custom folder icon',
dir: true,
folderIcon: {
closed: '📁',
open: '📂'
// a react element will also work
}
},
]
export function CustomFileIcons() {
return (
<TreeFolder tree={treeFolder} />
);
}
Custom Icons Result
default icon
⚽unicode icon
Hireact element as icon
Custom Component
import { TypeAnimation } from 'react-type-animation';
function MyAnimatedText({ text }:{ text: string }){
return <TypeAnimation
sequence={[
'One', // Types 'One'
1000, // Waits 1s
'Two', // Deletes 'One' and types 'Two'
2000, // Waits 2s
text, // Types text
3000 // wait 3s
]}
wrapper="span"
cursor={true}
repeat={Infinity}
/>
}
const treeFolder:Tree = [
{
text: 'aFile',
component: (text) => <MyAnimatedText text={text} />
},
{
text: 'aButtonFile',
component: (text) => <MyAnimatedText text={text} />,
onClick: () => alert('Hey, you!')
},
{
text: 'aDir',
component: (text) => <MyAnimatedText text={text} />,
dir: true,
open: true,
branch: [{ text: 'just a regular file' }]
},
]
export function CustomComponent() {
return (
<TreeFolder tree={treeFolder} />
);
}
Custom Component Result
just a regular file
Custom Colors
const treeFolder:Tree = [
{
text: 'custom color',
dir: true,
branch: [{ text: 'content' }],
// coloring for specific item
color: 'greenyellow',
hoverColor: 'lightskyblue',
},
{
text: 'file with custom color',
color: 'greenyellow',
// note the hover don't work
// since this is not a button
// (no onClick attribute)
hoverColor: 'lightskyblue',
},
{
text: 'global color',
dir: true,
branch: [{ text: 'content' }],
},
{
text: 'file',
},
]
export function CustomColors() {
return (
<TreeFolder
tree={treeFolder}
// coloring for all item
color='hotpink'
hoverColor='yellow'
// to color the side-line
borderColor='cyan'
/>
);
}
Custom Colors Result
content
file with custom color
content
file
OnClick Handler
const treeFolder:Tree = [
{
text: 'on a file',
onClick: () => alert('Clicked on a file!')
},
{
text: 'on a folder',
onClick: () => alert('Clicked on a folder!'),
dir: true,
branch: [{ text: 'folder content' }]
}
]
export function OnClickHandler() {
return (
<TreeFolder tree={treeFolder} />
);
}
Onclick Handler Result
folder content
Link
const treeFolder:Tree = [
{
text: 'link',
link: {
href: 'https://asdf.com',
// optional
newTab: true
},
// also optional
onClick: () => alert('this is a link')
},
]
export function ALink() {
return (
<TreeFolder tree={treeFolder} />
);
}
Link Result
Container Class
import { css } from '@emotion/css'
const borderedBox = css`
border-radius: 1rem;
border: 1px solid #42b342;
padding: 1rem;
`
const treeFolder:Tree = [
{
text: 'folder',
dir: true,
branch: [{ text: 'content' }]
},
{
text: 'file',
icon: '🥧'
},
]
export function ContainerClass() {
return (
<TreeFolder
tree={treeFolder}
containerClass={borderedBox} />
);
}
Container Class Result
content
🥧file
No Icons Variant
// use the no-icons variant
// when you want to use custom icon
// right from the start
import { TreeFolder, type Tree } from 'react-tree-folder/dist/no-icons'
import SvgFileIcon from './svg-file-icon'
const treeFolder:Tree = [
{
text: 'open this folder',
dir: true,
branch: [{ text: 'this is a file '}]
}
]
export function CustomIcons(){
return <TreeFolder
tree={treeFolder}
iconFolder={'📁'}
iconFolderOpen={'📂'}
iconFile={<SvgFileIcon />}
/>
}
Custom Icon Result
this is a file
File Sizes
vite v5.2.13 building for production...
✓ 6 modules transformed.
[vite:dts] Start generate declaration files...
dist/style.css 1.02 kB │ gzip: 0.46 kB
dist/no-icons.js 0.11 kB │ gzip: 0.13 kB │ map: 0.09 kB
dist/tree-mHxiewAB.js 3.24 kB │ gzip: 0.99 kB │ map: 7.00 kB
dist/index.js 4.09 kB │ gzip: 1.10 kB │ map: 5.55 kB
[vite:dts] Declaration files built in 409ms.
dist/style.css 1.02 kB │ gzip: 0.46 kB
dist/no-icons.cjs 0.19 kB │ gzip: 0.18 kB │ map: 0.09 kB
dist/tree-CMVc77ZW.cjs 1.92 kB │ gzip: 0.78 kB │ map: 6.45 kB
dist/index.cjs 3.43 kB │ gzip: 1.05 kB │ map: 5.20 kB
✓ built in 510ms
Last built locally. The packages built in CI may result in similar sizes.