New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

@pierre/trees

Package Overview
Dependencies
Maintainers
6
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@pierre/trees

beta
latest
npmnpm
Version
1.0.0-beta.6
Version published
Weekly downloads
0
Maintainers
6
Weekly downloads
 
Created
Source

@pierre/trees

Path-first file tree UI for the web.

@pierre/trees ships one implementation through four public entry points:

  • @pierre/trees — vanilla model, mounting API, prepared input helpers, icons, theming, and core types
  • @pierre/trees/react — React hooks and <FileTree model={...} />
  • @pierre/trees/ssr — preload helpers for declarative-shadow-DOM SSR
  • @pierre/trees/web-components — custom-element registration side effect

The tree renders inside a shadow root and keeps public state keyed by canonical path strings, not internal numeric IDs.

Install

pnpm add @pierre/trees

Vanilla usage

import { FileTree } from '@pierre/trees';

const mount = document.getElementById('mount')!;
mount.style.height = '320px';

const tree = new FileTree({
  flattenEmptyDirectories: true,
  initialExpansion: 'open',
  paths: ['README.md', 'src/index.ts', 'src/components/Button.tsx'],
  search: true,
});

tree.render({ containerWrapper: mount });

Common model methods include:

  • tree.add(path), tree.move(fromPath, toPath), tree.remove(path), and tree.resetPaths(paths)
  • tree.openSearch(), tree.setSearch(value), and tree.closeSearch()
  • tree.setGitStatus(entries) and tree.setIcons(config)
  • tree.getItem(path), tree.getSelectedPaths(), tree.getFocusedPath(), and tree.scrollToPath(path, { focus: false })
  • tree.cleanUp()

Prepared input

Prepare large or frequently reloaded path lists once, then pass the prepared result to FileTree.

import { FileTree, preparePresortedFileTreeInput } from '@pierre/trees';

const paths = ['src/', 'src/index.ts', 'README.md'];
const preparedInput = preparePresortedFileTreeInput(paths);

const tree = new FileTree({ preparedInput });

Use prepareFileTreeInput(paths) for raw input. Use preparePresortedFileTreeInput(paths) when the final order is already known.

React usage

'use client';

import { FileTree, useFileTree } from '@pierre/trees/react';

export function Example({ paths }: { paths: string[] }) {
  const { model } = useFileTree({
    initialExpansion: 'open',
    paths,
    search: true,
  });

  return (
    <FileTree
      model={model}
      header={<strong>Project files</strong>}
      renderContextMenu={(item) => <div>Menu for {item.path}</div>}
      style={{ height: '320px' }}
    />
  );
}

@pierre/trees/react exports FileTree, useFileTree, useFileTreeSearch, useFileTreeSelection, and useFileTreeSelector.

SSR

import { preloadFileTree } from '@pierre/trees/ssr';
import { FileTree, useFileTree } from '@pierre/trees/react';

const preloadedData = preloadFileTree({
  id: 'docs-tree',
  initialExpansion: 'open',
  paths: ['README.md', 'src/index.ts'],
  initialVisibleRowCount: 8,
});

export function HydratedTree() {
  const { model } = useFileTree({
    id: 'docs-tree',
    initialExpansion: 'open',
    paths: ['README.md', 'src/index.ts'],
    initialVisibleRowCount: 8,
  });

  return (
    <FileTree
      model={model}
      preloadedData={preloadedData}
      style={{ height: '240px' }}
    />
  );
}

preloadFileTree() returns FileTreeSsrPayload:

{
  id: string;
  outerStart: string;
  domOuterStart: string;
  shadowHtml: string;
  outerEnd: string;
}

Use ${payload.outerStart}${payload.shadowHtml}${payload.outerEnd} when the HTML parser will see the markup directly, such as a full server-rendered HTML response. Use ${payload.domOuterStart}${payload.shadowHtml}${payload.outerEnd} when inserting the full container string through DOM APIs like innerHTML or dangerouslySetInnerHTML. Pass { id, shadowHtml } to the React component as preloadedData.

Styling

The host element and shadow root read CSS variables such as:

  • --trees-selected-bg-override
  • --trees-border-color-override
  • --trees-fg-override
  • --trees-theme-*

Translate a Shiki or VS Code theme into tree CSS with themeToTreeStyles():

import { themeToTreeStyles } from '@pierre/trees';

const styles = themeToTreeStyles(theme);

If CSS variables are not enough, unsafeCSS injects raw CSS into the tree shadow root:

const tree = new FileTree({
  paths,
  unsafeCSS: `
    button[data-type='item'][data-item-selected] {
      border-radius: 999px;
    }
  `,
});

Treat unsafeCSS as an escape hatch. Start with host styles, CSS variables, and themeToTreeStyles() first.

Import the web-components entry point only when you need the custom element registration side effect:

import '@pierre/trees/web-components';

Icons, git status, and composition

The root package exports icon, git-status, context-menu, drag/drop, mutation, and row-decoration types. Public callbacks report canonical paths.

const tree = new FileTree({
  composition: {
    contextMenu: {
      enabled: true,
    },
  },
  paths,
});

When the context menu is enabled without an explicit triggerMode, it defaults to 'right-click'. Set triggerMode to 'button' or 'both' for the dedicated right-side action lane. In button-capable modes, buttonVisibility defaults to 'when-needed'; set it to 'always' to show decorative per-row affordances while the tree still uses one floating trigger button and one slotted menu surface.

renderRowDecoration owns a flexible row lane. Built-in git status uses the next fixed lane, so custom decoration content, git status, and the context-menu affordance can appear on the same row.

Development

From anywhere in the repo:

moonx trees:test
moonx trees:test-e2e
moonx trees:benchmark
moonx trees:benchmark-file-tree-get-item
moonx trees:profile-file-tree
moonx trees:typecheck
moonx trees:build

FAQs

Package last updated on 25 Jul 2026

Related posts