bippy

repository·main·Indexed 23 days ago

https://github.com/aidenybai/bippy

A toolkit for accessing and traversing React internals by pretending to be React DevTools. It allows developers to instrument React via the `instrument()` function to access the Fiber tree, inspect props, state, and context, and programmatically override them at runtime. Features include utilities for traversing rendered fibers, identifying fiber types, bridging DOM elements to fibers, and instrumenting React Refresh (HMR) for Vite, Next.js, and Metro.

Tokens
10K
Snippets
27
Records
59
Agent score
80%

What's inside bippy

  1. Glossary of React Fiber terms in bippy

    main

    Understanding these terms is essential for using bippy:

    • fiber: A "unit of execution" in React, representing a component or DOM element.
    • commit: The process of applying changes to the host tree (e.g., DOM mutations).
    • render: The process of building the fiber tree by executing component functions or classes.
    • host tree: The tree of UI elements that React mutates (e.g., DOM elements).
    • reconciler (or "renderer"): Custom bindings for React (e.g., react-dom, react-native, react-three-fiber) used to mutate the host tree.
    • rendererID: The ID of the reconciler, starting at 1.
    • root: A special FiberRoot type containing the container fiber in its current property.
  2. How React Fibers work in bippy

    main

    A React Fiber is a unit of execution representing either a component (function/class) or a host element (DOM node). Fibers contain the application's state, props, and context.

    While React's internal Fiber structure is complex and changes between versions, bippy provides stable utility functions to interact with them:

    • Traversal: Use traverseRenderedFibers to detect which fibers actually rendered, or traverseFiber to walk the entire tree (bypassing the need to manually follow child, sibling, and return pointers).
    • Data Access: Use traverseProps, traverseState, and traverseContexts to access data (bypassing the need to manually access memoizedProps, memoizedState, and dependencies).
    • Identity: Use setFiberId and getFiberId to assign and retrieve unique identities for fibers.
  3. Configure bippy in Next.js

    main

    In Next.js 15.3+, use the instrumentation-client.js file to ensure bippy loads before React hydration. Create this file at the root of your application (or inside the src folder if using a src directory structure).

    // instrumentation-client.ts
    import "bippy";
  4. Minimize bundle size for library maintainers

    main

    If you are building a library and want to minimize bundle size, you can avoid importing the full bippy package (~4 KB gzipped). Instead, use bippy/install-hook-only to install the hook without the utility functions, then import only the specific utilities you need from bippy/core.

    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
  5. Install and initialize bippy

    main

    Install bippy via npm. Critical Requirement: You must import bippy before any React code is executed in your application. Bippy works by hijacking the window.__REACT_DEVTOOLS_GLOBAL_HOOK__ property, which React uses to report its internals. If imported too late, React will not attach its internals to the hook, and bippy will not function.

    npm install bippy
  6. Configure bippy in Vite

    main

    In Vite, import bippy at the very top of your main entry point (e.g., 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
  7. Install the React DevTools hook via safelyInstallRDTHook

    main

    To instrument your application with bippy, you must ensure the __REACT_DEVTOOLS_GLOBAL_HOOK__ is loaded before React is executed. Use safelyInstallRDTHook() to initialize the hook in client environments. This function is typically used as a side effect at the very top of your entry point.

    CRITICAL: This must be imported and called before any other imports that might trigger React execution (e.g., importing components or your main App file).

  8. Use instrument lifecycle handlers

    main

    The instrument function accepts an options object with the following lifecycle hooks:

    • onCommitFiberRoot(rendererID, root): Called when React is ready to commit a fiber root (i.e., it has rendered the app and is ready to apply changes to the host tree like DOM mutations).
    • onPostCommitFiberRoot(rendererID, root): Called after React has committed a fiber root and effects have run.
    • onCommitFiberUnmount(rendererID, fiber): Called when a specific fiber unmounts.

    Parameters:

    • rendererID: The ID of the reconciler (e.g., react-dom), starting at 1. Multiple reconcilers may exist.
    • root: A FiberRoot object containing the container fiber in its current property.
    • fiber: The specific fiber being unmounted.
  9. Instrument React with bippy

    main

    Use the instrument function to install the React DevTools global hook. This allows you to intercept React fiber lifecycle events by providing custom handlers.

    Important: instrument must be imported BEFORE react in your application entry point to ensure the hook is successfully installed.

  10. Inspect and Identify Fibers

    main

    Use these utilities to query properties and identity of React fibers:

    • isValidFiber(fiber): Checks if an object is a valid React Fiber.
    • getDisplayName(fiber): Returns the component's display name.
    • getType(fiber): Returns the underlying component definition (e.g., the function or class).
    • isHostFiber(fiber): Returns true if the fiber is a host fiber (e.g., a DOM node).
    • isCompositeFiber(fiber): Returns true if the fiber is a composite fiber (e.g., a component).
    • getFiberId(fiber) / setFiberId(fiber): Manages a persistent identity for fibers (which are anonymous by default).
    • getFiberStack(fiber): Returns an array representing the stack of fibers from the current fiber up to the root [fiber, fiber.return, ...].
  11. Traverse the Fiber Tree

    main

    Bippy provides several utilities to walk the React fiber tree:

    • traverseFiber(root, callback): Calls the callback on every fiber in the tree.
    • traverseRenderedFibers(root, callback): Traverses only the fibers that have actually rendered.
    • traverseProps(fiber, callback): Traverses the props of a specific fiber. The callback receives (propName, next, prev).
    • traverseState(fiber, callback): Traverses state (useState, useReducer, etc.) and effects that set state. The callback receives (next, prev).
    • traverseContexts(fiber, callback): Traverses the contexts (useContext) of a fiber. The callback receives (next, prev).

    Note: For traverseFiber and traverseRenderedFibers, you must call instrument before React to ensure the hooks are patched.

    import { instrument, traverseRenderedFibers } from "bippy"; // must be imported BEFORE react
    import * as React from "react";
    
    instrument({
      onCommitFiberRoot(rendererID, root) {
        traverseRenderedFibers(root, (fiber) => {
          console.log("fiber rendered", fiber);
        });
      },
    });