A development tool to explore, inspect, and diagnose your React Native apps.
—
Provides Storybook switcher component for toggling between your React Native app and Storybook interface, enabling seamless component development and testing.
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();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;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
};
}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>
);/**
* 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;
}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;
}The plugin processes Storybook commands and forwards them to the React component:
onCommand: (command) => {
if (command.type === "storybook") {
emitter.emit("storybook", command.payload);
}
}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);// 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"
/>
));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>
);
};
};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} />;// Shared Storybook configuration
const createApp = (StorybookUI) => {
const reactotron = Reactotron
.configure({ name: "TeamApp" })
.useReactNative({ storybook: true })
.connect();
return Reactotron.storybookSwitcher(StorybookUI)(BaseApp);
};// 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);From Reactotron desktop, you can:
// 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: {} }); // ToggleUsage 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