Claude
Skills
Sign in
Back

state-inspector

Included with Lifetime
$97 forever

Detect the frontend framework (React, Vue, Angular, Svelte) and inspect application state: React fiber tree traversal with component props/state/hooks via __REACT_DEVTOOLS_GLOBAL_HOOK__, Vue instance tree via __VUE__, and store inspection for Redux, Vuex, Pinia, Zustand, MobX, and Jotai. Supports state snapshots before/after user actions for diffing.

Design

What this skill does


# State Inspector

Detect which frontend framework and state management libraries a page uses,
then inspect their internal state. Traverse component trees, read props and
state, snapshot store contents, and diff state changes across user actions.

## When to Use

- Debugging component state without installing browser devtools extensions.
- Understanding the component hierarchy and data flow of an unfamiliar app.
- Verifying that a user action correctly updates application state.
- Inspecting Redux/Vuex/Pinia/Zustand store contents.
- Diffing state before and after an interaction to trace data flow.

## Prerequisites

- **Playwright MCP server** connected and responding (all `mcp__playwright__browser_*` tools available).
- Target page must use a detectable frontend framework (React, Vue, Angular, Svelte, or vanilla JS with state management libraries).
- React DevTools hook detection works best when React is built in development mode. Production builds may have limited fiber tree access.

## Workflow

### Step 1 -- Navigate to the Target Page

```
browser_navigate({ url: "<target_url>" })
```

```
browser_wait_for({ time: 2 })
```

### Step 2 -- Detect Frontend Framework and State Libraries

```javascript
browser_evaluate({
  function: `() => {
    const detection = {
      framework: null,
      version: null,
      stateManagement: [],
      details: {}
    };

    // --- React ---
    if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
      detection.framework = 'React';
      const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
      if (hook.renderers && hook.renderers.size > 0) {
        const renderer = hook.renderers.values().next().value;
        detection.version = renderer.version || 'unknown';
      }
      detection.details.fiberRootCount = hook.getFiberRoots ? hook.getFiberRoots(1)?.size || 0 : 'N/A';
    } else if (document.querySelector('[data-reactroot], [data-reactid]')) {
      detection.framework = 'React';
      detection.version = 'production (no devtools hook)';
    }

    // --- Vue ---
    if (window.__VUE__) {
      detection.framework = detection.framework ? detection.framework + ' + Vue' : 'Vue';
      detection.version = window.__VUE__.version || 'unknown';
    } else if (window.__vue_app__) {
      detection.framework = detection.framework ? detection.framework + ' + Vue' : 'Vue';
      detection.details.vueApp = true;
    } else if (document.querySelector('[data-v-]') || document.querySelector('.__vue-inspector-container')) {
      detection.framework = detection.framework ? detection.framework + ' + Vue' : 'Vue';
      detection.version = 'detected via data-v- attributes';
    }

    // --- Angular ---
    if (window.ng || window.getAllAngularRootElements) {
      detection.framework = detection.framework ? detection.framework + ' + Angular' : 'Angular';
      if (window.ng && window.ng.getComponent) detection.details.angularIvy = true;
    }

    // --- Svelte ---
    if (document.querySelector('[class*="svelte-"]')) {
      detection.framework = detection.framework ? detection.framework + ' + Svelte' : 'Svelte';
    }

    // --- State Management ---
    // Redux
    if (window.__REDUX_DEVTOOLS_EXTENSION__) {
      detection.stateManagement.push('Redux DevTools Extension');
    }
    // Check for Redux store on common locations
    const reduxStore = window.__REDUX_STORE__ || window.store;
    if (reduxStore && typeof reduxStore.getState === 'function' && typeof reduxStore.dispatch === 'function') {
      detection.stateManagement.push('Redux (global store)');
    }

    // Zustand
    if (window.__zustand_stores || document.querySelector('[data-zustand]')) {
      detection.stateManagement.push('Zustand');
    }

    // MobX
    if (window.__mobxGlobals || window.__mobxInstanceCount) {
      detection.stateManagement.push('MobX');
    }

    // Vuex
    if (window.__VUE__ && window.__vue_app__ && window.__vue_app__.config && window.__vue_app__.config.globalProperties.$store) {
      detection.stateManagement.push('Vuex');
    }

    // Pinia
    if (window.__pinia) {
      detection.stateManagement.push('Pinia');
    }

    // Jotai / Recoil (hard to detect without devtools)
    // Check for common patterns

    if (!detection.framework) {
      detection.framework = 'None detected (vanilla JS or unrecognized framework)';
    }

    return detection;
  }`
})
```

### Step 3 -- Traverse React Fiber Tree (if React detected)

Walk the React fiber tree to extract component hierarchy, props, and state.

```javascript
browser_evaluate({
  function: `() => {
    const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
    if (!hook || !hook.getFiberRoots) {
      return { error: 'React DevTools hook not available. App may be in production mode.' };
    }

    const roots = hook.getFiberRoots(1);
    if (!roots || roots.size === 0) {
      return { error: 'No fiber roots found' };
    }

    const root = roots.values().next().value;
    const tree = [];
    let nodeCount = 0;
    const maxNodes = 100;

    function getHooksState(fiber) {
      const hooks = [];
      let hook = fiber.memoizedState;
      let idx = 0;
      while (hook && idx < 10) {
        let value = hook.memoizedState;
        // Simplify complex objects
        if (value && typeof value === 'object') {
          try {
            const str = JSON.stringify(value);
            if (str.length > 500) value = str.substring(0, 500) + '...';
            else value = JSON.parse(str);
          } catch {
            value = '[Complex Object]';
          }
        }
        hooks.push({ index: idx, value: value });
        hook = hook.next;
        idx++;
      }
      return hooks;
    }

    function walkFiber(fiber, depth) {
      if (!fiber || nodeCount >= maxNodes) return;

      // Only include function/class components, skip host elements
      if (typeof fiber.type === 'function' || (fiber.type && fiber.type.$$typeof)) {
        nodeCount++;
        const name = fiber.type.displayName || fiber.type.name || 'Anonymous';

        let props = {};
        if (fiber.memoizedProps) {
          try {
            const p = {};
            for (const [key, val] of Object.entries(fiber.memoizedProps)) {
              if (key === 'children') { p.children = typeof val === 'string' ? val.substring(0, 50) : '[children]'; continue; }
              if (typeof val === 'function') { p[key] = '[Function]'; continue; }
              if (typeof val === 'object' && val !== null) {
                try { const s = JSON.stringify(val); p[key] = s.length > 200 ? s.substring(0, 200) + '...' : JSON.parse(s); }
                catch { p[key] = '[Object]'; }
                continue;
              }
              p[key] = val;
            }
            props = p;
          } catch { props = '[Error reading props]'; }
        }

        const hooks = getHooksState(fiber);

        tree.push({
          depth: depth,
          name: name,
          key: fiber.key || null,
          props: props,
          hooks: hooks.length > 0 ? hooks : null,
          hasState: hooks.length > 0
        });
      }

      // Traverse children
      walkFiber(fiber.child, typeof fiber.type === 'function' ? depth + 1 : depth);
      // Traverse siblings
      walkFiber(fiber.sibling, depth);
    }

    walkFiber(root.current, 0);

    return {
      totalComponents: nodeCount,
      maxDepth: Math.max(...tree.map(n => n.depth), 0),
      tree: tree
    };
  }`
})
```

### Step 4 -- Inspect Vue Instance Tree (if Vue detected)

```javascript
browser_evaluate({
  function: `() => {
    // Vue 3
    const app = window.__vue_app__;
    if (!app) {
      // Try to find Vue instance from DOM
      const el = document.querySelector('[__vue_app__]') || document.getElementById('app');
      if (el && el.__vue_app__) {
        window.__vue_app__ = el.__vue_app__;
      } else {
        return { error: 'Vue app instance not found' };
      }
    }

    const tree = [];
    let count = 0;
    const maxNodes = 100;

    functi

Related in Design