Skip to content

FileTree ​

A tree of file paths, drawn in the library's theme on the @pierre/trees engine. useFileTree creates the model, and FileTree.Root draws it, with a sideways shift for names that do not fit.

tsx
import { FileTree, useFileTree } from '@adecore/ui';
tsx
import { useState } from 'react';
import { FileTree, useFileTree } from '@adecore/ui';

export default function FileTreeDemo() {
    const [viewed, setViewed] = useState<ReadonlySet<string>>(new Set());
    const [opened, setOpened] = useState<string | null>(null);

    const { model } = useFileTree({
        paths: ['src/components/Button.tsx', 'src/components/Checkbox.tsx', 'src/utilities/path.ts', 'README.md'],
        initialExpansion: 'open',
        flattenEmptyDirectories: true
    });

    return (
        <div className="w-80">
            <FileTree.Root
                model={model}
                label="Project files"
                onActivate={setOpened}
                className="h-48"
                renderControl={(row) =>
                    row.kind === 'file' ? (
                        <FileTree.Checkbox
                            label={`Viewed ${row.path}`}
                            checked={viewed.has(row.path)}
                            onCheckedChange={(checked) =>
                                setViewed((current) => {
                                    const next = new Set(current);

                                    if (checked) {
                                        next.add(row.path);
                                    } else {
                                        next.delete(row.path);
                                    }

                                    return next;
                                })
                            }
                        />
                    ) : null
                }
                renderDecoration={(row) => (row.kind === 'file' ? <span className="font-mono text-xs text-text-faint">+2</span> : null)}
            />
            {opened !== null && <p className="mt-2 truncate text-xs text-text-muted">{opened}</p>}
        </div>
    );
}

For nodes that are not file paths, such as connections or database objects, use Tree, which shares the look and leaves the model to you.

A file browser ​

tsx
import { useState } from 'react';
import { FileTree, useFileTree } from '@adecore/ui';

export function ProjectFiles() {
    const [opened, setOpened] = useState<string | null>(null);
    const { model } = useFileTree({
        paths: ['src/index.ts', 'src/utilities/path.ts', 'README.md'],
        initialExpansion: 'open',
        flattenEmptyDirectories: true
    });

    return (
        <div>
            <FileTree.Root model={model} label="Project files" className="h-64" onActivate={setOpened} />
            {opened !== null && <p>{opened}</p>}
        </div>
    );
}

Paths are POSIX and relative to a root you choose. A directory path ends in /; the model derives parent directories. Keep absolute paths and permission checks in your own code.

The view virtualizes, so it needs a height to scroll in. With fitContent it takes the height of its rows instead, up to its container's height; do not give it a height then. Without label the tree is named "Files" (tree.label); give it a name that says which files it holds.

Defaults and ownership ​

useFileTree starts with closed directories, directories first in numeric order (FileTree.compareRows), the FileIcon set (FILE_TREE_ICONS), and the engine's search, sticky folders and context menu off. You can override each. Density and row height are fixed. unsafeCSS is appended to the wrapper's styles inside the shadow root.

The options only create the model. A new paths or initialExpansion on a later render does not rebuild it; use the model's methods (resetPaths, add, remove, move, batch, setGitStatus, setIcons).

tsx
import { useEffect } from 'react';
import { FileTree, useFileTree } from '@adecore/ui';

export function Files({ paths }: { paths: readonly string[] }) {
    const { model } = useFileTree({ paths });

    useEffect(() => {
        model.resetPaths(paths);
    }, [model, paths]);

    return <FileTree.Root model={model} label="Files" className="h-64" />;
}

resetPaths replaces the listing and its expansion; Models, loading and expansion shows how to keep folds. The model never touches the filesystem.

Your code owns filesystem changes, listings, search requests, caches and permissions. The model owns the displayed paths, selection and expansion. The hook cleans up its model on unmount, so never call cleanUp() yourself. Unsubscribe your own listeners and cancel your requests when the root changes.

Guides ​

Limits ​

Screen reader behavior across the row control slots is not verified yet. The wrapper relies on internals of the pinned engine version.

Licenses and third-party notices are listed in each package.