npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-tree-inspector

v0.1.1

Published

An experimental React Fiber tree inspector overlay for development and testing workflows.

Readme

react-tree-inspector

An experimental Higher-Order Component (HOC) and standalone React component that injects a floating, interactive development overlay powered by the React Fiber internals. Open the overlay to see a real-time, collapsible tree of your entire component architecture — with props, context subscriptions, and hook counts displayed per selected node.

Status: testing only — This package is currently intended for local experimentation and validation. It is not ready for production use.

Development-only — This package accesses React Fiber internals (__reactFiber$ / __reactInternalInstance$). It is intended exclusively for development and debugging environments.


Features

| Feature | Detail | | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Fiber-powered tree crawl | Accesses the live Fiber tree — captures functional components, class components, context wrappers, HOC wrappers, and React.memo nodes that React.Children misses entirely | | Component kind classification | HostComponent, FunctionComponent, ClassComponent, ContextProvider/Consumer, ForwardRef, Memo, HOC, Suspense — each colour-coded in the tree | | HOC detection | Detects higher-order wrappers via displayName patterns: with*, Connect(…), inject(…), observer(…), _class* | | Per-component dependency profile | Props (key/value), context subscriptions (via dependencies.firstContext), and active hook count (via memoizedState linked-list) | | Portal-based overlay | Rendered via createPortal directly on document.body, bypassing any parent overflow: hidden clipping | | Keyboard navigation | Arrow keys to expand/collapse, Enter/Space to select, Escape to close | | Zero runtime deps | Only react and react-dom as peer dependencies | | Dual ESM + CJS output | Ships dist/index.esm.js, dist/index.cjs.js, and dist/index.d.ts | | React 17 / 18 / 19 | Handles both legacy ReactDOM.render roots and createRoot concurrent roots |


Installation

npm install react-tree-inspector
# peer deps (if not already installed)
npm install react react-dom

Usage

Option A — Higher-Order Component (recommended for app-level inspection)

Wrap your root component once. The HOC adds a floating button and crawls the tree starting from its own mount point.

// src/App.tsx or src/main.tsx
import { withTreeInspector } from "react-tree-inspector";

function App() {
  return <YourApplication />;
}

export default withTreeInspector(App, {
  buttonPosition: "bottom-right", // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
  maxDepth: 40, // cap Fiber recursion depth
  defaultOpen: false, // auto-open on mount
  exclude: /^Suspense|^Router/, // filter out noisy wrappers
});

Gate it with an env check so it never ships in production:

const ExportedApp =
  process.env.NODE_ENV === "development"
    ? withTreeInspector(App, { buttonPosition: "bottom-right" })
    : App;

export default ExportedApp;

Option B — Standalone <TreeInspector> component

Drop it once, anywhere in the tree. By default it crawls from #root / #app for a full-app view.

import { TreeInspector } from "react-tree-inspector";

function App() {
  return (
    <>
      <YourApplication />
      {process.env.NODE_ENV === "development" && (
        <TreeInspector buttonPosition="bottom-right" maxDepth={30} />
      )}
    </>
  );
}

Scope it to a specific subtree via targetRef:

import { useRef } from "react";
import { TreeInspector } from "react-tree-inspector";

function Dashboard() {
  const dashboardRef = useRef<HTMLDivElement>(null);

  return (
    <div ref={dashboardRef}>
      <ComplexDashboard />
      <TreeInspector targetRef={dashboardRef} buttonPosition="bottom-left" />
    </div>
  );
}

Option C — Headless Fiber crawl

Use the raw crawler for custom integrations (e.g. automated structural tests, snapshot diffing).

import { crawlFiberTree } from "react-tree-inspector";
import type { InspectorNode } from "react-tree-inspector";

const container = document.getElementById("root")!;
const { tree, error } = crawlFiberTree(container, { maxDepth: 20 });

if (error) {
  console.error("Crawl failed:", error);
} else if (tree) {
  console.log(JSON.stringify(tree, null, 2));
}

API Reference

withTreeInspector(WrappedComponent, config?)

Returns a new component with the inspector overlay injected.

| Config field | Type | Default | Description | | ---------------- | -------------------------------------------------------------- | ---------------- | -------------------------------------- | | maxDepth | number | 50 | Maximum Fiber recursion depth | | exclude | RegExp | undefined | Skip nodes whose resolved name matches | | buttonPosition | 'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left' | 'bottom-right' | Corner for the floating button | | defaultOpen | boolean | false | Auto-open the panel on mount |


<TreeInspector {...config} targetRef? />

Accepts all TreeInspectorConfig fields plus:

| Prop | Type | Default | Description | | ----------- | ---------------------------------- | ----------- | -------------------------------------------------------------------------------- | | targetRef | React.RefObject<Element \| null> | undefined | Specific element to crawl from. Falls back to #root#appdocument.body |


crawlFiberTree(element, options?)

function crawlFiberTree(
  rootElement: Element,
  options?: { maxDepth?: number; exclude?: RegExp },
): CrawlResult;

interface CrawlResult {
  tree: InspectorNode | null;
  error: string | null;
}

InspectorNode

interface InspectorNode {
  id: string; // Stable path-based ID ("root-0-1-2")
  name: string; // Component name or HTML tag
  kind: ComponentKind; // Classification (see below)
  depth: number; // Nesting depth from root
  children: InspectorNode[]; // Ordered child nodes
  dependencyProfile: DependencyProfile;
}

interface DependencyProfile {
  props: PropEntry[]; // { key, value } pairs (children omitted)
  contextDependencies: ContextDependency[]; // Active context subscriptions
  hookCount: number; // Count of hooks in memoizedState chain
  displayName: string; // Resolved display name
}

type ComponentKind =
  | "HostComponent" // DOM element
  | "FunctionComponent" // FC
  | "ClassComponent" // CC
  | "ContextProvider" // Context.Provider
  | "ContextConsumer" // Context.Consumer
  | "ForwardRef" // React.forwardRef
  | "Memo" // React.memo
  | "HOC" // Higher-Order Component (inferred by name)
  | "Fragment" // React.Fragment
  | "Suspense" // React.Suspense
  | "Unknown";

Building Locally

cd react-tree-inspector
npm install
npm run build       # Compile to dist/
npm run dev         # Watch mode
npm run typecheck   # Validate TypeScript without emitting

Link to a consumer project

# Inside react-tree-inspector/
npm link

# Inside your consumer app/
npm link react-tree-inspector

When linked, import normally:

import { withTreeInspector } from "react-tree-inspector";

How it works

DOM element
    │  Object.keys() scan for __reactFiber$<key>
    │                        or __reactContainer$<key>
    ▼
FiberNode (HostRoot)
    │  Skip HostRoot, walk .child / .sibling chain
    ▼
Component FiberNodes
    │  Per node:
    │    tag  → ComponentKind
    │    type → display name
    │    memoizedProps  → PropEntry[]
    │    dependencies.firstContext → ContextDependency[]
    │    memoizedState (linked list) → hookCount
    ▼
InspectorNode tree  →  TreeViewer + InspectorPanel rendered via createPortal

Fiber property keys

| React version | Internal element key | Root container key | | ------------- | -------------------------------- | ------------------------------------------- | | React 16/17 | __reactInternalInstance$<rand> | _reactRootContainer._internalRoot.current | | React 18/19 | __reactFiber$<rand> | __reactContainer$<rand> |

The crawler scans Object.keys() for all of these, making it resilient to the random suffix.


Caveats

  • React internals are not a public API. The __reactFiber$ property and Fiber node shape may change in future React versions. All reads are guarded with try/catch and optional chaining — if React changes something, the affected field returns a safe empty value rather than crashing your app.
  • Production builds strip Fiber references. React's production bundle removes the __reactFiber$ attachment. Always gate this package behind process.env.NODE_ENV === 'development'.
  • Concurrent mode: Context and hook metadata is read from the committed Fiber state (memoizedState / dependencies), which reflects the last rendered output.

License

MIT