Skip to main content
WebSocket support allows you to remotely control your on-device Storybook from a browser or external tools. This enables real-time synchronization between multiple Storybook instances.

Overview

The WebSocket server provides:
  • Remote story selection: Control which story is displayed from external tools
  • Event broadcasting: Send custom events to all connected clients
  • Story index API: Fetch the list of all available stories
  • MCP support: AI agent integration for component documentation (experimental)

Metro Configuration

Enable WebSockets in your Metro configuration:
metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const { withStorybook } = require('@storybook/react-native/metro/withStorybook');

const config = getDefaultConfig(__dirname);

module.exports = withStorybook(config, {
  websockets: 'auto',
});

WebSocket Options

interface WebsocketsOptions {
  port?: number;
  host?: string;
}
port
number
default:"7007"
The port the WebSocket server will listen on.
withStorybook(config, {
  websockets: { port: 7007 },
});
host
string
default:"localhost"
The host the WebSocket server will bind to.Set to 'auto' to automatically use your local IP address for LAN connections.
// Auto-detect host IP
withStorybook(config, {
  websockets: 'auto',
});

// Specific host
withStorybook(config, {
  websockets: { host: '0.0.0.0', port: 7007 },
});
When host is 'auto', the server binds to all network interfaces and automatically detects your local IP address for the client connection.

Client Configuration

Enable WebSocket connections in your app:
index.tsx
import { start } from '@storybook/react-native';

const view = start({
  annotations,
  storyEntries,
});

const StorybookUI = view.getStorybookUI({
  enableWebsockets: true,
  host: 'localhost',
  port: 7007,
});

export default StorybookUI;

Client Options

enableWebsockets
boolean
default:"false"
Enable WebSocket connections to the server.
view.getStorybookUI({
  enableWebsockets: true,
});
host
string
default:"Platform dependent"
The WebSocket server host to connect to.
  • Android emulator: Defaults to 10.0.2.2 (host machine)
  • iOS simulator & physical devices: Defaults to localhost
If WebSockets are configured in Metro config, this value is automatically injected.
view.getStorybookUI({
  enableWebsockets: true,
  host: '192.168.1.100', // Your computer's IP
});
port
number
default:"7007"
The WebSocket server port to connect to.If WebSockets are configured in Metro config, this value is automatically injected.
view.getStorybookUI({
  enableWebsockets: true,
  port: 7007,
});
secured
boolean
default:"false"
Use secure WebSocket connections (wss:// instead of ws://).
view.getStorybookUI({
  enableWebsockets: true,
  secured: true,
});
query
string
default:"undefined"
Optional query string to append to the WebSocket URL.
view.getStorybookUI({
  enableWebsockets: true,
  query: 'token=abc123',
});

Server API

The WebSocket server provides several HTTP endpoints:

GET /index.json

Returns the story index with all available stories.
curl http://localhost:7007/index.json
Response:
{
  "v": 5,
  "entries": {
    "button--primary": {
      "type": "story",
      "id": "button--primary",
      "name": "Primary",
      "title": "Button",
      "importPath": "./components/Button.stories.tsx",
      "tags": ["story"]
    }
  }
}

POST /send-event

Sends an event to all connected WebSocket clients.
curl -X POST http://localhost:7007/send-event \
  -H "Content-Type: application/json" \
  -d '{"type":"setCurrentStory","args":[{"storyId":"button--primary"}]}'
This endpoint requires WebSockets to be enabled. It returns a 503 error if WebSockets are disabled.

WebSocket Connection

Connect to ws://localhost:7007 for real-time bidirectional communication.
const ws = new WebSocket('ws://localhost:7007');

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  console.log('Received:', message);
};

ws.send(JSON.stringify({
  type: 'setCurrentStory',
  args: [{ storyId: 'button--primary' }]
}));
The server broadcasts all received messages to all connected clients (except the sender).

Examples

Complete Configuration

metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const { withStorybook } = require('@storybook/react-native/metro/withStorybook');

const config = getDefaultConfig(__dirname);

module.exports = withStorybook(config, {
  websockets: {
    port: 7007,
    host: 'localhost',
  },
});
index.tsx
import { start } from '@storybook/react-native';

const view = start({ annotations, storyEntries });

const StorybookUI = view.getStorybookUI({
  enableWebsockets: true,
  host: 'localhost',
  port: 7007,
});

export default StorybookUI;
Use 'auto' to automatically configure WebSockets with sensible defaults:
metro.config.js
module.exports = withStorybook(config, {
  websockets: 'auto',
});
The server will:
  • Listen on port 7007
  • Auto-detect your local IP address
  • Inject the configuration into your app automatically

LAN Testing

Test your Storybook on physical devices over your local network:
metro.config.js
module.exports = withStorybook(config, {
  websockets: {
    port: 7007,
    host: '0.0.0.0', // Bind to all interfaces
  },
});
index.tsx
const StorybookUI = view.getStorybookUI({
  enableWebsockets: true,
  host: '192.168.1.100', // Your computer's LAN IP
  port: 7007,
});

Troubleshooting

Port Already in Use

If you see the error:
[Storybook] Port 7007 is already in use. The channel server will not start.
Another instance is already running. Either:
  • Stop the other instance
  • Use a different port
withStorybook(config, {
  websockets: { port: 7008 },
});

Connection Failed

If WebSocket connections fail:
  1. Check the host: Ensure the host is reachable from your device
  2. Firewall: Verify your firewall allows connections on the WebSocket port
  3. Network: Ensure both devices are on the same network (for LAN testing)

Android Emulator

The Android emulator uses a special IP address to reach the host machine:
view.getStorybookUI({
  enableWebsockets: true,
  host: '10.0.2.2', // Host machine from Android emulator
  port: 7007,
});

Heartbeat

The WebSocket server sends a ping message every 10 seconds to keep connections alive:
{"type": "ping", "args": []}
Clients don’t need to respond to ping messages.

Build docs developers (and LLMs) love