Skip to content

Feature: Trees - Make Pierre trees more extensible with a headless core, pluggable row lanes, and richer renderer hooks #691

@gpetrov

Description

@gpetrov

Prerequisites

Proposal

Pierre trees already has a strong foundation for large-scale tree UIs: a path-first model, a performant store, virtualization, search, SSR, and a custom-element boundary. That makes it very compelling not only as a file tree, but potentially as a more general high-performance tree platform.

The main limitation today is not the engine. It is the extensibility surface around rendering, interaction, and composition.

I would like to propose evolving Pierre trees toward a more explicitly extensible architecture, while preserving the performance-oriented path-first design it already has.

At a high level, the proposal is:

1. Expose a clearer headless core

The internal state/controller/store side of Pierre trees is already strong. It would be valuable to make that a more explicit public foundation that can be used independently of the default renderer.

This would allow consumers to:

  • reuse the tree engine without being forced into the built-in row renderer
  • build custom renderers in Lit, React, Preact, or plain DOM
  • keep UI and state fully separated while still using the same selection, focus, expansion, mutation, and search model

The goal is not to remove the current renderer, but to make it one renderer on top of a reusable core.

2. Replace the single decoration hook with a lane-based plugin model

Today the public extension surface around row rendering is still fairly narrow. It supports a header, a context menu composition surface, icons/theming, and a single decoration hook.

That works for simple badges and icons, but many advanced tree use cases need more than one decoration lane and more than one extension point.

A better model would be an ordered row-lane architecture, for example:

  • leading
  • icon
  • content
  • status
  • trailing
  • action
  • overlay

Plugins should be able to contribute to one or more lanes without taking over the whole row.

That would allow:

  • multiple independent status systems
  • validation and warning markers
  • execution or output indicators
  • custom action affordances
  • workflow-specific placeholders or insertion affordances

3. Separate row-state hooks from row-render hooks

A lot of extensions do not need custom DOM. They only need to:

  • mark a row disabled
  • contribute attributes or classes
  • provide titles/tooltips
  • expose warning or validation state
  • participate in selection, focus, or interaction policy

These use cases would be much better served by pure row-state hooks.

Then row-render hooks can be reserved for visible-row rendering only.

This keeps the API cleaner and makes performance easier to reason about.

4. Support richer row contributions than icon-or-text decorations

For many applications, a row needs more than a single extra icon or text badge.

It would help if plugins could contribute richer lane content, while still fitting into a controlled and performant rendering model.

This does not need to mean arbitrary uncontrolled rendering everywhere. It could still be structured:

  • path-keyed plugin state
  • visible-row-only render hooks
  • bounded lane render targets
  • optional richer row overrides for advanced consumers

5. Add drag-and-drop extension hooks

The built-in drag-and-drop support is useful, but advanced trees often need more than standard file-tree semantics.

It would help if plugins could participate in:

  • custom drop target resolution
  • before / inside / after insertion semantics
  • placeholder-style drop targets
  • move / copy / create / insert / reorder semantics
  • drag preview customization
  • hover-open behavior
  • cross-tree drag-and-drop policies

This would make Pierre trees much more useful for structured editors, builders, and workflow-style trees.

6. Add search and filtering provider hooks

The current search behavior is already useful, but advanced consumers often need tree search to include more than the visible path string.

It would help if plugins could contribute:

  • aliases
  • tags
  • searchable metadata
  • hidden keywords
  • domain-specific filtering logic
  • optional async or worker-backed search adapters for very large datasets

This would keep the default search simple while making the tree more broadly useful for application-specific data.

7. Move built-ins onto the same extensibility path

A strong way to prove the plugin model would be to migrate existing built-in renderer features onto it.

For example, built-in git status could ideally become a first-party plugin using the same public hook system that downstream consumers would use.

That would ensure the extensibility model is real and powerful enough, not just an extra compatibility layer.

8. Preserve strict performance invariants

This proposal is only valuable if it keeps the current strengths of Pierre trees intact.

Any extensibility model should preserve:

  • visible-window-first rendering
  • path-first state
  • compatibility with virtualization
  • no full-DOM scanning assumptions
  • predictable row measurement
  • plugin state keyed by canonical path rather than DOM identity
  • pure or cacheable hooks where possible

The engine should remain fast first.

Motivation and context

Pierre trees already feels stronger internally than its current public renderer API suggests.

The underlying architecture is already doing many of the difficult things correctly:

  • path-first public identity rather than DOM or numeric-ID coupling
  • a store designed for large trees
  • virtualization and slice-first rendering
  • SSR and custom-element support
  • built-in search, drag-and-drop, and mutation handling

That is exactly why this feature request seems worth making.

The gap is that the public extension model is still closer to “configuration hooks for a finished component” than “a reusable tree platform”.

That is enough for standard file-tree use cases, but it becomes limiting when a product needs:

  • multiple concurrent statuses
  • richer per-row structure
  • domain-specific controls
  • custom DnD semantics
  • custom filtering/search behavior
  • alternative interaction modes
  • strict separation of UI from state

A good example is built-in git status support. It is helpful, but it is also a sign of the current limitation: it works as a built-in special case, not as proof that consumers can extend the tree in the same way.

For advanced applications, the desired model is usually:

  • one strong state/store/controller layer
  • multiple possible renderers
  • multiple plugins contributing status, actions, metadata, and interaction behavior
  • a renderer that remains fast because all of that still operates inside a virtualization-friendly, path-first model

That would make Pierre trees much more attractive for:

  • IDE-like products
  • visual builders
  • workflow editors
  • structured editors
  • large application trees that are not purely file-system-like

In other words, this proposal is not really asking for more built-in product features.

It is asking for the renderer and extensibility model to become as strong and reusable as the engine underneath it already is.

If this direction sounds interesting, I would be happy to help refine it further into a more API-shaped proposal, for example around:

  • headless controller boundaries
  • row-state hooks
  • lane contribution APIs
  • DnD provider hooks
  • search provider hooks
  • plugin ordering and composition

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions