CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-reactotron-react-native

A development tool to explore, inspect, and diagnose your React Native apps.

Pending
Overview
Eval results
Files

storybook.mddocs/

Storybook Integration

Provides Storybook switcher component for toggling between your React Native app and Storybook interface, enabling seamless component development and testing.

Capabilities

Storybook Plugin

Creates a plugin that provides a React component switcher for integrating Storybook with your React Native app.

/**
 * Create Storybook integration plugin
 * @returns Plugin creator function (no configuration options)
 */
function storybook(): PluginCreator;

Usage Examples:

import Reactotron, { storybook } from "reactotron-react-native";

// Basic Storybook integration
const reactotron = Reactotron
  .configure({ name: "MyApp" })
  .use(storybook())
  .connect();

// Via useReactNative
const reactotron = Reactotron
  .configure({ name: "MyApp" })
  .useReactNative({
    storybook: true
  })
  .connect();

Plugin Features

The Storybook plugin provides a switcher component for toggling between your app and Storybook.

/**
 * Storybook plugin features
 */
interface StorybookFeatures {
  /** 
   * Creates a switcher component that toggles between app and Storybook
   * @param storybookUi - Your Storybook UI component
   * @returns Higher-order component for app/Storybook switching
   */
  storybookSwitcher: (
    storybookUi: React.ComponentType
  ) => (WrappedComponent: React.ComponentType<any>) => React.ComponentType<any>;
}

Usage Examples:

import React from "react";
import { getStorybookUI, configure } from "@storybook/react-native";
import Reactotron from "reactotron-react-native";

// Configure Storybook
configure(() => {
  require("./stories");
}, module);

// Get Storybook UI
const StorybookUIRoot = getStorybookUI({});

// Your main app component
const MyApp = () => (
  <View>
    <Text>My React Native App</Text>
  </View>
);

// Create switcher component
const AppWithStorybook = Reactotron.storybookSwitcher(StorybookUIRoot)(MyApp);

export default AppWithStorybook;

Storybook Commands

Toggle Storybook Command

The plugin responds to commands from Reactotron to toggle between app and Storybook modes:

/**
 * Command to toggle Storybook visibility
 */
interface StorybookCommand {
  type: "storybook";
  payload: {
    show?: boolean;  // true to show Storybook, false to show app
  };
}

React Component Integration

Switcher Component Structure

The switcher creates a container component that manages the visibility of your app vs Storybook:

const StorybookSwitcherContainer = (props) => (
  <StorybookSwitcher storybookUi={StorybookUIRoot} emitter={emitter}>
    <WrappedComponent {...props} />
  </StorybookSwitcher>
);

StorybookSwitcher Props

/**
 * Props for the StorybookSwitcher component
 */
interface StorybookSwitcherProps {
  /** Storybook UI component to display when in Storybook mode */
  storybookUi: React.ComponentType;
  /** Event emitter for receiving toggle commands */
  emitter: Emitter;
  /** Your app component (passed as children) */
  children: React.ReactNode;
}

Event System

Mitt Event Emitter

The plugin uses the mitt library for event communication between Reactotron and the React component:

/**
 * Storybook event emitter interface
 */
interface StorybookEmitter {
  /** Listen for Storybook toggle events */
  on(type: "storybook", handler: (payload: any) => void): void;
  /** Remove Storybook event listener */
  off(type: "storybook", handler: (payload: any) => void): void;
  /** Emit Storybook toggle event */
  emit(type: "storybook", payload: any): void;
}

Command Processing

The plugin processes Storybook commands and forwards them to the React component:

onCommand: (command) => {
  if (command.type === "storybook") {
    emitter.emit("storybook", command.payload);
  }
}

Storybook Setup Integration

Complete Setup Example

import React from "react";
import { AppRegistry } from "react-native";
import { getStorybookUI, configure } from "@storybook/react-native";
import Reactotron from "reactotron-react-native";

// Configure Reactotron
const reactotron = Reactotron
  .configure({ name: "MyApp" })
  .useReactNative({ storybook: true })
  .connect();

// Configure Storybook
configure(() => {
  require("./src/components/Button/Button.stories");
  require("./src/components/Card/Card.stories");
  // ... other story files
}, module);

const StorybookUIRoot = getStorybookUI({
  port: 7007,
  host: "localhost",
  onDeviceUI: true,
  asyncStorage: require("@react-native-async-storage/async-storage").default
});

// Main app component
const App = () => {
  // Your app implementation
};

// Development vs Production
let AppToRender = App;

if (__DEV__) {
  // Add Storybook switcher in development
  AppToRender = Reactotron.storybookSwitcher(StorybookUIRoot)(App);
}

AppRegistry.registerComponent("MyApp", () => AppToRender);

Story File Example

// Button.stories.tsx
import React from "react";
import { storiesOf } from "@storybook/react-native";
import { action } from "@storybook/addon-actions";
import { Button } from "./Button";

storiesOf("Button", module)
  .add("Primary", () => (
    <Button
      title="Primary Button"
      onPress={action("primary-pressed")}
      variant="primary"
    />
  ))
  .add("Secondary", () => (
    <Button
      title="Secondary Button"
      onPress={action("secondary-pressed")}
      variant="secondary"
    />
  ));

Implementation Details

HOC Pattern

The Storybook switcher uses a Higher-Order Component pattern similar to the overlay plugin:

const createStorybookSwitcher = (storybookUi) => (WrappedComponent) => {
  return function StorybookSwitcherContainer(props) {
    return (
      <StorybookSwitcher storybookUi={storybookUi} emitter={emitter}>
        <WrappedComponent {...props} />
      </StorybookSwitcher>
    );
  };
};

State Management

The switcher component manages the visibility state internally:

const [showStorybook, setShowStorybook] = useState(false);

useEffect(() => {
  const handler = (payload) => {
    if (typeof payload.show === 'boolean') {
      setShowStorybook(payload.show);
    } else {
      setShowStorybook(prev => !prev);  // Toggle
    }
  };
  
  emitter.on("storybook", handler);
  return () => emitter.off("storybook", handler);
}, []);

return showStorybook ? <StorybookUI /> : <WrappedComponent {...props} />;

Usage Scenarios

Component Development Workflow

  1. Development: Write components with Storybook stories
  2. Testing: Use Reactotron to toggle to Storybook mode
  3. Debugging: Switch back to app to test integration
  4. Iteration: Rapid switching between isolated component testing and app testing

Team Collaboration

// Shared Storybook configuration
const createApp = (StorybookUI) => {
  const reactotron = Reactotron
    .configure({ name: "TeamApp" })
    .useReactNative({ storybook: true })
    .connect();
    
  return Reactotron.storybookSwitcher(StorybookUI)(BaseApp);
};

Multiple Storybook Instances

// Different Storybook configurations for different modules
const DesignSystemStorybook = getStorybookUI({
  // Design system stories
});

const FeatureStorybook = getStorybookUI({
  // Feature-specific stories  
});

// Toggle between different Storybook instances
const App = Reactotron.storybookSwitcher(
  process.env.STORYBOOK_MODE === 'design-system' 
    ? DesignSystemStorybook 
    : FeatureStorybook
)(BaseApp);

Integration with Reactotron Desktop

Toggle Controls

From Reactotron desktop, you can:

  1. Show Storybook: Send command to display Storybook interface
  2. Hide Storybook: Send command to return to main app
  3. Toggle: Switch between current state

Command Examples

// From Reactotron desktop console
reactotron.send({ type: "storybook", payload: { show: true } });   // Show Storybook
reactotron.send({ type: "storybook", payload: { show: false } });  // Show App
reactotron.send({ type: "storybook", payload: {} });               // Toggle

Usage Best Practices:

// Development-only Storybook integration
const setupStorybook = () => {
  if (!__DEV__) return null;
  
  try {
    const StorybookUI = getStorybookUI({
      port: 7007,
      host: "localhost"
    });
    
    return Reactotron.storybookSwitcher(StorybookUI);
  } catch (error) {
    console.warn("Storybook setup failed:", error);
    return null;
  }
};

const storybookWrapper = setupStorybook();
const App = storybookWrapper ? storybookWrapper(BaseApp) : BaseApp;

// Conditional registration
AppRegistry.registerComponent("MyApp", () => App);

Install with Tessl CLI

npx tessl i tessl/npm-reactotron-react-native

docs

async-storage.md

core-configuration.md

dev-tools.md

editor-integration.md

error-tracking.md

global-logging.md

index.md

networking.md

overlay.md

storybook.md

tile.json