Skip to content

Storybook Integration

Use the Storybook entrypoint when Itera should load a Storybook manager URL such as http://127.0.0.1:6006/?path=/story/... instead of a direct iframe.html story URL.

This is the validated React Storybook flow: the manager window relays inspector traffic into the active preview iframe, while the preview bridge and iteration runtime stay inside the preview surface.

What this supports

  • preview-only inspection for React Storybook previews
  • Storybook manager URLs without host-side iframe.html URL rewriting
  • direct iframe.html fallback when you intentionally want to bypass the manager relay

Out of scope:

  • inspecting Storybook manager panels, toolbars, or addon UI
  • support claims beyond the React Storybook 10 manager/preview contract validated in itera_design_system

Install

The Storybook helpers ship in the same React package:

bash
npm install @iteraai/react-component-inspector @iteraai/inspector-protocol react react-dom

1. Add a shared preview bootstrap

Read the base Itera host allowlist from VITE_ITERA_COMPONENT_INSPECTOR_HOST_ORIGINS, then derive the Storybook manager origin from the preview referrer. When the preview is loaded directly through iframe.html, the resolver falls back to the explicit allowlist from that env var.

ts
import { bootIterationInspectorRuntime } from '@iteraai/react-component-inspector/iterationInspector';
import {
  bootstrapStorybookPreviewInspectorBridge,
  resolveStorybookPreviewHostOrigins,
} from '@iteraai/react-component-inspector/storybook';

const COMPONENT_INSPECTOR_LOG_PREFIX = '[itera/storybook/component-inspector]';
const STORYBOOK_COMPONENT_INSPECTOR_BOOTSTRAPPED_FLAG =
  '__ITERA_STORYBOOK_COMPONENT_INSPECTOR_BOOTSTRAPPED__';

export const STORYBOOK_COMPONENT_INSPECTOR_HOST_ORIGINS =
  import.meta.env.VITE_ITERA_COMPONENT_INSPECTOR_HOST_ORIGINS;

type GlobalState = typeof globalThis & {
  [STORYBOOK_COMPONENT_INSPECTOR_BOOTSTRAPPED_FLAG]?: boolean;
};

const globalState = globalThis as GlobalState;

export const bootstrapStorybookComponentInspector = () => {
  if (globalState[STORYBOOK_COMPONENT_INSPECTOR_BOOTSTRAPPED_FLAG] === true) {
    return false;
  }

  try {
    const previewHostOrigins = resolveStorybookPreviewHostOrigins({
      hostOrigins: STORYBOOK_COMPONENT_INSPECTOR_HOST_ORIGINS,
      referrer: document.referrer || undefined,
    });

    bootIterationInspectorRuntime();

    const bridge = bootstrapStorybookPreviewInspectorBridge({
      enabled: true,
      hostOrigins: previewHostOrigins,
    });

    globalState[STORYBOOK_COMPONENT_INSPECTOR_BOOTSTRAPPED_FLAG] = true;

    window.addEventListener(
      'beforeunload',
      () => {
        bridge.destroy();
      },
      { once: true },
    );

    return true;
  } catch (error) {
    console.error(
      `${COMPONENT_INSPECTOR_LOG_PREFIX} Failed to bootstrap the Storybook preview inspector.`,
      error,
    );

    return false;
  }
};

2. Start the manager relay once

Boot the relay from .storybook/manager.ts so the manager window can forward both itera-component-inspector and itera:iteration-inspector traffic to the active preview iframe.

ts
import { initStorybookManagerRelay } from '@iteraai/react-component-inspector/storybook';
import { STORYBOOK_COMPONENT_INSPECTOR_HOST_ORIGINS } from './storybook-bootstrap';

const COMPONENT_INSPECTOR_LOG_PREFIX = '[itera/storybook/component-inspector]';
const STORYBOOK_MANAGER_RELAY_BOOTSTRAPPED_FLAG =
  '__ITERA_STORYBOOK_MANAGER_RELAY_BOOTSTRAPPED__';

type GlobalState = typeof globalThis & {
  [STORYBOOK_MANAGER_RELAY_BOOTSTRAPPED_FLAG]?: boolean;
};

const globalState = globalThis as GlobalState;

if (globalState[STORYBOOK_MANAGER_RELAY_BOOTSTRAPPED_FLAG] !== true) {
  try {
    initStorybookManagerRelay({
      hostOrigins: STORYBOOK_COMPONENT_INSPECTOR_HOST_ORIGINS,
    });
    globalState[STORYBOOK_MANAGER_RELAY_BOOTSTRAPPED_FLAG] = true;
  } catch (error) {
    console.error(
      `${COMPONENT_INSPECTOR_LOG_PREFIX} Failed to bootstrap the Storybook manager relay.`,
      error,
    );
  }
}

initStorybookManagerRelay(...) targets the Storybook 10 preview iframe selector iframe#storybook-preview-iframe by default. If your Storybook shell replaces or wraps that iframe, pass previewIframeSelector or resolvePreviewIframe.

3. Call the preview bootstrap from .storybook/preview.tsx

Start the preview wrapper at module load so the runtime and preview bridge are ready before stories become interactive.

tsx
import { bootstrapStorybookComponentInspector } from './storybook-bootstrap';

void bootstrapStorybookComponentInspector();

Notes

  • Keep VITE_ITERA_COMPONENT_INSPECTOR_HOST_ORIGINS limited to exact Itera host origins. The preview resolver adds the Storybook manager origin intentionally; do not replace the explicit host allowlist with the Storybook origin.
  • If document.referrer is missing or rewritten in your preview, pass managerOrigin explicitly to resolveStorybookPreviewHostOrigins(...) or bootstrapStorybookPreviewInspectorBridge(...).
  • For non-Storybook React apps, stay on the standard React integration.

Customer integration guidance for the Itera platform.