MCPcopy Index your code
hub / github.com/aidenybai/bippy

github.com/aidenybai/bippy @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
500 symbols 1,197 edges 126 files 0 documented · 0% 3 cross-repo links
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

[!WARNING] ⚠️⚠️⚠️ this project may break production apps and cause unexpected behavior ⚠️⚠️⚠️

this project uses react internals, which can change at any time. it is not recommended to depend on internals unless you really, really have to. by proceeding, you acknowledge the risk of breaking your own code or apps that use your code.

bippy

version downloads

bippy is a toolkit to hack into react internals

by default, you cannot access react internals. bippy bypasses this by "pretending" to be react devtools, giving you access to the fiber tree and other internals.

  • works outside of react – no react code modification needed
  • utility functions that work across modern react (v17-19)
  • no prior react source code knowledge required
import { onCommitFiberRoot, traverseFiber } from "bippy"; // must be imported BEFORE react

instrument({
  onCommitFiberRoot: (root) => {
    traverseFiber(root.current, (fiber) => {
      // prints every fiber in the current React tree
      console.log("fiber:", fiber);
    });
  },
});

how it works & motivation

bippy allows you to access and use react fibers outside of react components.

a react fiber is a "unit of execution." this means react will do something based on the data in a fiber. each fiber either represents a composite (function/class component) or a host (dom element).

here is a live visualization of what the fiber tree looks like, and here is a deep dive article.

fibers are useful because they contain information about the react app (component props, state, contexts, etc.). a simplified version of a fiber looks roughly like this:

interface Fiber {
  // component type (function/class)
  type: any;

  child: Fiber | null;
  sibling: Fiber | null;

  // stateNode is the host fiber (e.g. DOM element)
  stateNode: Node | null;

  // parent fiber
  return: Fiber | null;

  // the previous or current version of the fiber
  alternate: Fiber | null;

  // saved props input
  memoizedProps: any;

  // state (useState, useReducer, useSES, etc.)
  memoizedState: any;

  // contexts (useContext)
  dependencies: Dependencies | null;

  // effects (useEffect, useLayoutEffect, etc.)
  updateQueue: any;
}

here, the child, sibling, and return properties are pointers to other fibers in the tree.

additionally, memoizedProps, memoizedState, and dependencies are the fiber's props, state, and contexts.

while all of the information is there, it's not super easy to work with, and changes frequently across different versions of react. bippy simplifies this by providing utility functions like:

  • traverseRenderedFibers to detect renders and traverseFiber to traverse the overall fiber tree
  • (instead of child, sibling, and return pointers)
  • traverseProps, traverseState, and traverseContexts to traverse the fiber's props, state, and contexts
  • (instead of memoizedProps, memoizedState, and dependencies)

however, fibers aren't directly accessible by the user. so, we have to hack our way around to accessing it.

luckily, react reads from a property in the window object: window.__REACT_DEVTOOLS_GLOBAL_HOOK__ and runs handlers on it when certain events happen. this property must exist before react's bundle is executed. this is intended for react devtools, but we can use it to our advantage.

here's what it roughly looks like:

interface __REACT_DEVTOOLS_GLOBAL_HOOK__ {
  // list of renderers (react-dom, react-native, etc.)
  renderers: Map<RendererID, reactRenderer>;

  // called when react has rendered everything for an update and the fiber tree is fully built and ready to
  // apply changes to the host tree (e.g. DOM mutations)
  onCommitFiberRoot: (rendererID: RendererID, root: FiberRoot, commitPriority?: number) => void;

  // called when effects run
  onPostCommitFiberRoot: (rendererID: RendererID, root: FiberRoot) => void;

  // called when a specific fiber unmounts
  onCommitFiberUnmount: (rendererID: RendererID, fiber: Fiber) => void;
}

bippy works by monkey-patching window.__REACT_DEVTOOLS_GLOBAL_HOOK__ with our own custom handlers. bippy simplifies this by providing utility functions like:

  • instrument to safely patch window.__REACT_DEVTOOLS_GLOBAL_HOOK__
  • (instead of directly mutating onCommitFiberRoot, ...)
  • secure to wrap your handlers in a try/catch and determine if handlers are safe to run
  • (instead of rawdogging window.__REACT_DEVTOOLS_GLOBAL_HOOK__ handlers, which may crash your app)
  • traverseRenderedFibers to traverse the fiber tree and determine which fibers have actually rendered
  • (instead of child, sibling, and return pointers)
  • traverseFiber to traverse the fiber tree, regardless of whether it has rendered
  • (instead of child, sibling, and return pointers)
  • setFiberId / getFiberId to set and get a fiber's id
  • (instead of anonymous fibers with no identity)

how to use

we recommend installing via npm.

this package should be imported before a React app runs. this will add a special object to the global which is used by React for providing its internals to the tool for analysis (React Devtools does the same). as soon as React library is loaded and attached to the tool, bippy starts collecting data about what is going on in React's internals.

npm install bippy

since bippy needs to be imported before react, some bundlers require specific configuration to ensure the correct import order.

next.js

in next.js 15.3+, use the instrumentation-client.js file to ensure bippy loads before react. create this file at the root of your application (or inside the src folder if you're using the src directory structure):

// instrumentation-client.ts
import "bippy";

this file executes before react hydration, making it the ideal place to initialize bippy.

vite

in vite, import bippy at the very top of your main entry point (typically src/main.tsx or src/main.ts) before any react imports:

// src/main.tsx
import "bippy";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

// ... rest of your code

the import order is critical: bippy must be imported before any react packages.

note for library maintainers: if you're building a library and want to define your own utility functions while minimizing bundle size, you can use bippy/install-hook-only (~90 bytes) instead of the main bippy export. this only installs the react devtools hook without importing any utility functions, allowing you to import only what you need from bippy/core or define your own fiber utilities. that said, the full bippy package is only ~4kb gzipped, so bundle size is rarely a concern.

```typescript import "bippy/install-hook-only"; // only installs the hook import { getRDTHook, traverseFiber } from "bippy/core"; // import only what you need import * as React from "react"; // import react AFTER the hook is installed

const hook = getRDTHook(); // define your own utilities or use only specific ones ```

API reference

instrument

patches window.__REACT_DEVTOOLS_GLOBAL_HOOK__ with your handlers. must be imported before react, and must be initialized to properly run any other methods.

use with the secure function to prevent uncaught errors from crashing your app.

import { instrument, secure } from "bippy"; // must be imported BEFORE react
import * as React from "react";

instrument(
  secure({
    onCommitFiberRoot(rendererID, root) {
      console.log("root ready to commit", root);
    },
    onPostCommitFiberRoot(rendererID, root) {
      console.log("root with effects committed", root);
    },
    onCommitFiberUnmount(rendererID, fiber) {
      console.log("fiber unmounted", fiber);
    },
  }),
);

getRDTHook

returns the window.__REACT_DEVTOOLS_GLOBAL_HOOK__ object. great for advanced use cases, such as accessing or modifying the renderers property.

import { getRDTHook } from "bippy";

const hook = getRDTHook();
console.log(hook);

traverseRenderedFibers

not every fiber in the fiber tree renders. traverseRenderedFibers allows you to traverse the fiber tree and determine which fibers have actually rendered.

import { instrument, secure, traverseRenderedFibers } from "bippy"; // must be imported BEFORE react
import * as React from "react";

instrument(
  secure({
    onCommitFiberRoot(rendererID, root) {
      traverseRenderedFibers(root, (fiber) => {
        console.log("fiber rendered", fiber);
      });
    },
  }),
);

traverseFiber

calls a callback on every fiber in the fiber tree.

import { instrument, secure, traverseFiber } from "bippy"; // must be imported BEFORE react
import * as React from "react";

instrument(
  secure({
    onCommitFiberRoot(rendererID, root) {
      traverseFiber(root.current, (fiber) => {
        console.log(fiber);
      });
    },
  }),
);

traverseProps

traverses the props of a fiber.

import { traverseProps } from "bippy";

// ...

traverseProps(fiber, (propName, next, prev) => {
  console.log(propName, next, prev);
});

traverseState

traverses the state (useState, useReducer, etc.) and effects that set state of a fiber.

import { traverseState } from "bippy";

// ...

traverseState(fiber, (next, prev) => {
  console.log(next, prev);
});

traverseContexts

traverses the contexts (useContext) of a fiber.

import { traverseContexts } from "bippy";

// ...

traverseContexts(fiber, (next, prev) => {
  console.log(next, prev);
});

setFiberId / getFiberId

set and get a persistent identity for a fiber. by default, fibers are anonymous and have no identity.

import { setFiberId, getFiberId } from "bippy";

// ...

setFiberId(fiber);
console.log("unique id for fiber:", getFiberId(fiber));

isHostFiber

returns true if the fiber is a host fiber (e.g., a DOM node in react-dom).

import { isHostFiber } from "bippy";

if (isHostFiber(fiber)) {
  console.log("fiber is a host fiber");
}

isCompositeFiber

returns true if the fiber is a composite fiber. composite fibers represent class components, function components, memoized components, and so on (anything that can actually render output).

import { isCompositeFiber } from "bippy";

if (isCompositeFiber(fiber)) {
  console.log("fiber is a composite fiber");
}

getDisplayName

returns the display name of the fiber's component, falling back to the component's function or class name if available.

import { getDisplayName } from "bippy";

console.log(getDisplayName(fiber));

getType

returns the underlying type (the component definition) for a given fiber. for example, this could be a function component or class component.

import { getType } from "bippy";
import { memo } from "react";

const RealComponent = () => {
  return 

hello

;
};
const MemoizedComponent = memo(() => {
  return 

hello

;
});

console.log(getType(fiberForMemoizedComponent) === RealComponent);

getNearestHostFiber / getNearestHostFibers

getNearestHostFiber returns the closest host fiber above or below a given fiber. getNearestHostFibers(fiber) returns all host fibers associated with the provided fiber and its subtree.

import { getNearestHostFiber, getNearestHostFibers } from "bippy";

// ...

function Component() {
  return (
    <>


hello




world


    </>
  );
}

console.log(getNearestHostFiber(fiberForComponent)); // 

hello


console.log(getNearestHostFibers(fiberForComponent)); // [

hello

, 

world

]

getTimings

returns the self and total render times for the fiber.

// timings don't exist in react production builds
if (fiber.actualDuration !== undefined) {
  const { selfTime, totalTime } = getTimings(fiber);
  console.log(selfTime, totalTime);
}

getFiberStack

returns an array representing the stack of fibers from the current fiber up to the root.

[fiber, fiber.return, fiber.return.return, ...]

getMutatedHostFibers

returns an array of all host fibers that have committed and rendered in the provided fiber's subtree.

import { getMutatedHostFibers } from "bippy";

console.log(getMutatedHostFibers(fiber));

isValidFiber

returns true if the given object is a valid React Fiber (i.e., has a tag, stateNode, return, child, sibling, etc.).

import { isValidFiber } from "bippy";

console.log(isValidFiber(fiber));

getFiberFromHostInstance

returns the fiber associated with a given host instance (e.g., a DOM element).

import { getFiberFromHostInstance } from "bippy";

const fiber = getFiberFromHostInstance(document.querySelector("div"));
console.log(fiber);

getLatestFiber

returns the latest fiber (since it may be double-buffered). usually use this in combination with getFiberFromHostInstance.

```typescript import { getLatestFiber } from "bippy";

const latestFiber = getLatestFiber(getFiberFromHostIn

Extension points exported contracts — how you extend this code

EditorState (Interface)
(no doc)
packages/vite-playground/src/source-editor.tsx
Todo (Interface)
(no doc)
packages/next-playground/components/todo-item.tsx
InstrumentationOptions (Interface)
(no doc)
packages/bippy/src/core.ts
CopyButtonProps (Interface)
(no doc)
packages/website/components/copy-button.tsx
Window (Interface)
(no doc)
packages/e2e/fixtures/next-app/app/test-harness.tsx
Source (Interface)
(no doc)
packages/bippy/src/types.ts
BippyFeature (Interface)
(no doc)
packages/website/components/project-info.tsx
Window (Interface)
(no doc)
packages/e2e/fixtures/vite-app/src/main.tsx

Core symbols most depended-on inside this repo

cn
called by 98
packages/website/lib/utils.ts
instrument
called by 64
packages/bippy/src/core.ts
extractHookVariableName
called by 43
packages/bippy/src/source/parse-hook-names.ts
pushHookLogEntry
called by 22
packages/bippy/src/source/inspect-hooks.ts
traverseFiber
called by 21
packages/bippy/src/core.ts
nextHook
called by 21
packages/bippy/src/source/inspect-hooks.ts
getDisplayName
called by 18
packages/bippy/src/core.ts
normalizeFileName
called by 18
packages/bippy/src/source/get-source.ts

Shape

Function 419
Interface 65
Class 10
Method 6

Languages

TypeScript100%

Modules by API surface

packages/bippy/src/source/inspect-hooks.ts56 symbols
packages/bippy/src/core.ts48 symbols
packages/bippy/src/types.ts26 symbols
packages/bippy/src/source/owner-stack.ts18 symbols
packages/website/components/ui/dropdown-menu.tsx16 symbols
packages/website/components/ui/context-menu.tsx15 symbols
packages/bippy/src/source/symbolication.ts14 symbols
packages/bippy/src/source/parse-stack.ts14 symbols
packages/bippy/src/rdt-hook.ts14 symbols
packages/bippy/src/source/parse-hook-names.ts12 symbols
packages/bippy/src/test/inspect-hooks.test.tsx11 symbols
packages/website/components/ui/sheet.tsx10 symbols

For agents

$ claude mcp add bippy \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact