
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
@aiquants/directory-tree
Advanced tools
High-performance directory tree component for React with virtual scrolling and file selection
A high-performance React directory tree component with virtualization, file selection, and theming support.
npm install @aiquants/directory-tree
# or
yarn add @aiquants/directory-tree
# or
pnpm add @aiquants/directory-tree
This package requires the following peer dependencies:
npm install react react-dom @aiquants/virtualscroll tailwind-variants tailwind-merge
import React from 'react';
import { DirectoryTree, useDirectoryTreeState } from '@aiquants/directory-tree';
import { useTheme } from './hooks/useTheme'; // Your theme hook
import type { DirectoryEntry } from '@aiquants/directory-tree';
const sampleData: DirectoryEntry[] = [
{
name: 'src',
absolutePath: '/src',
relativePath: 'src',
children: [
{
name: 'components',
absolutePath: '/src/components',
relativePath: 'src/components',
children: [
{
name: 'App.tsx',
absolutePath: '/src/components/App.tsx',
relativePath: 'src/components/App.tsx',
children: null
}
]
}
]
}
];
export default function App() {
const { theme } = useTheme();
const {
toggle,
isExpanded,
expandMultiple,
collapseMultiple,
isPending
} = useDirectoryTreeState({
storageKey: 'my-directory-tree'
});
// Calculate line color based on theme
const lineColor = theme === "dark" ? "#4A5568" : "#A0AEC0";
const handleFileSelect = (absolutePath: string, relativePath: string) => {
console.log(`File selected: ${absolutePath} (${relativePath})`);
};
return (
<div className="h-96 w-full border rounded-lg">
<DirectoryTree
entries={sampleData}
expansion={{
toggle,
isExpanded,
expandMultiple,
collapseMultiple,
isPending
}}
selection={{
onFileSelect: handleFileSelect,
selectedPath: null
}}
visual={{
lineColor,
className: "h-full"
}}
/>
</div>
);
}
The main component for rendering the directory tree.
| Prop | Type | Required | Description |
|---|---|---|---|
entries | DirectoryEntry[] | Yes | Array of root directory entries to display |
expansion | object | Yes | Configuration for tree expansion state and behavior |
selection | object | Yes | Configuration for item selection |
visual | object | No | Visual customization options |
virtualScroll | DirectoryTreeVirtualScrollOptions | No | Pass-through options for the underlying VirtualScroll component |
expansion)| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
toggle | (path: string, relativePath: string) => void | Yes | - | Function to toggle directory expansion state |
isExpanded | (path: string) => boolean | Yes | - | Function to check if a directory is expanded |
expandMultiple | (paths: string[]) => void | Yes | - | Function to expand multiple directories |
collapseMultiple | (paths: string[]) => void | Yes | - | Function to collapse multiple directories |
isPending | boolean | No | false | Whether the tree is in a pending state |
alwaysExpanded | boolean | No | false | If true, all directories are always expanded |
doubleClickAction | 'recursive' | 'toggle' | No | 'recursive' | Action on double-clicking a directory |
selection)| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
onFileSelect | (absolutePath: string, relativePath: string) => void | Yes | - | Callback function triggered when a file is selected |
selectedPath | string | null | No | - | The currently selected file path |
mode | 'none' | 'single' | 'multiple' | No | 'none' | Selection mode for items |
selectedItems | Set<string> | No | - | Set of paths for currently selected items |
onSelectionChange | (path: string, isSelected: boolean) => void | No | - | Callback when item selection changes |
visual)| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
className | string | No | - | Optional CSS class name for the container |
style | React.CSSProperties | No | - | Optional inline styles for the container |
lineColor | string | No | '#A0AEC0' | The color of the tree lines |
showTreeLines | boolean | No | true | Flag indicating whether to render tree connector lines |
showExpandIcons | boolean | No | true | Flag indicating whether to render directory expand icons |
showDirectoryIcons | boolean | No | true | Flag indicating whether to render directory type icons |
showFileIcons | boolean | No | true | Flag indicating whether to render file type icons |
iconOverrides | DirectoryTreeIconOverrides | No | - | Icon overrides applied globally |
expandIconSize | number | No | - | Size of the expand icon |
removeRootIndent | boolean | No | false | If true, removes the indentation and connector lines for root-level items |
virtualScroll lets you customize the embedded @aiquants/virtualscroll instance without re-implementing list rendering. Every option is optional and mirrors the VirtualScroll API.
overscanCount: Adjust how many items render beyond the viewport for smoother scrolling (default: 10).scrollBarOptions: Configure scrollbar appearance and behavior (width, thumb drag, track click, arrow buttons, tap scroll circle).behaviorOptions: Configure scrolling behavior (pointer drag, keyboard navigation, inertia, wheel speed).onScroll, onRangeChange, className, background, initialScrollIndex, initialScrollOffset: Hook into scroll lifecycle or provide bespoke styling.Example:
<DirectoryTree
{...commonProps}
virtualScroll={{
overscanCount: 6,
behaviorOptions: {
enablePointerDrag: false,
},
scrollBarOptions: {
width: 14,
tapScrollCircleOptions: {
radius: 32
}
}
}}
/>;
A hook for managing directory tree state with localStorage persistence.
| Parameter | Type | Description |
|---|---|---|
options | UseDirectoryTreeStateProps | Configuration options |
| Option | Type | Description |
|---|---|---|
initialExpanded | Set<string> | Initially expanded directories |
storageKey | string | localStorage key for persistence (default: 'directory-tree-state') |
| Property | Type | Description |
|---|---|---|
expanded | Set<string> | Currently expanded directories |
toggle | (path: string, relativePath: string) => void | Toggle directory expansion |
isExpanded | (path: string) => boolean | Check if directory is expanded |
expand | (path: string) => void | Expand a directory |
collapse | (path: string) => void | Collapse a directory |
expandMultiple | (paths: string[]) => void | Expand multiple directories |
collapseMultiple | (paths: string[]) => void | Collapse multiple directories |
collapseAll | () => void | Collapse all directories |
isPending | boolean | Whether a transition is pending |
type DirectoryEntry = {
name: string;
absolutePath: string;
relativePath: string;
children: DirectoryEntry[] | null;
};
The component uses Tailwind CSS for styling. Make sure you have Tailwind CSS configured in your project. The component supports both light and dark themes, but theme control is managed by the calling component.
The lineColor prop allows you to control the tree line color based on your application's theme:
import { useTheme } from './hooks/useTheme';
function MyComponent() {
const { theme } = useTheme();
// Calculate line color based on theme
const lineColor = theme === "dark" ? "#4A5568" : "#A0AEC0";
return (
<DirectoryTree
// ... other props
visual={{
lineColor: lineColor
}}
/>
);
}
You can customize the appearance by overriding the default Tailwind classes:
<DirectoryTree
visual={{
className: "custom-directory-tree",
style: { height: '400px' }
}}
// ... other props
/>
.custom-directory-tree {
/* Your custom styles */
}
The component is optimized for large datasets through virtualization:
import { DirectoryTree } from '@aiquants/directory-tree';
// Handle thousands of entries efficiently
<DirectoryTree
entries={largeDataset}
// ... other required props
visual={{
style: { height: '600px' }
}}
/>
Enable multiple selection for batch operations:
const [selectedItems, setSelectedItems] = useState(new Set<string>());
const handleSelectionChange = (path: string, isSelected: boolean) => {
setSelectedItems(prev => {
const newSet = new Set(prev);
if (isSelected) {
newSet.add(path);
} else {
newSet.delete(path);
}
return newSet;
});
};
<DirectoryTree
// ... other props
selection={{
mode: "multiple",
selectedItems: selectedItems,
onSelectionChange: handleSelectionChange,
onFileSelect: handleFileSelect // Required prop
}}
/>
Control how directories behave on double-click:
<DirectoryTree
// ... other props
expansion={{
// ... required expansion props
doubleClickAction: "toggle" // Only toggle the clicked directory
// or
doubleClickAction: "recursive" // Expand/collapse all children (default)
}}
/>
The useDirectoryTreeState hook automatically persists expansion state to localStorage:
const { toggle, isExpanded, expandMultiple, collapseMultiple } = useDirectoryTreeState({
storageKey: 'myapp-directory-tree',
initialExpanded: new Set(['/src', '/docs'])
});
This package is written in TypeScript and provides comprehensive type definitions. All components and hooks are fully typed for the best development experience.
We welcome contributions! Please feel free to submit issues and pull requests.
MIT License - see the LICENSE file for details.
Made with ❤️ by the AIQuants team.
FAQs
High-performance directory tree component for React with virtual scrolling and file selection
We found that @aiquants/directory-tree demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.