
React components for embedding Mobilerun's native streaming.
Live WebRTC video + full scrcpy remote control of a cloud device, in any React app.
Drop a live, controllable cloud device (iOS/ Android) into any React app:
- 📺
<DeviceStream />— WebRTC video with touch, swipe and keyboard control, plus automatic reconnects with exponential backoff and a retry UI once attempts are exhausted. - 🪝
useDeviceStream()— resolves a device's stream credentials from@mobilerun/sdk, polling while it boots. - 🧭
<NavigationBar />,<StreamStatusPill />,<RemoteControl />— the surrounding chrome and a lower-level escape hatch. - 🎨 Prebuilt stylesheet — no Tailwind setup required, themeable via CSS variables.
- 📦 Typed — ships its own
.d.ts; React and the SDK are peer dependencies.
npm install @mobilerun/react @mobilerun/sdk react react-domreact, react-dom and @mobilerun/sdk are peer dependencies.
The SDK's Device already carries everything the stream needs (streamUrl + streamToken). useDeviceStream fetches them and refreshes them when the stream reconnects:
⚠️ MOBILERUN_API_KEYis a secret API key — never ship it to the browser. It grants full account access, so the snippet below assumes a server-only context (RSC, route handler, SSR). In a client component, resolvestreamUrl+streamTokenon your backend instead and pass only those to<DeviceStream />— they're short-lived and scoped to a single device. See Without the hook.
importMobilerunfrom'@mobilerun/sdk';import{DeviceStream,useDeviceStream}from'@mobilerun/react';import'@mobilerun/react/styles.css';constclient=newMobilerun({apiKey: process.env.MOBILERUN_API_KEY});exportfunctionDevicePanel({ deviceId }: {deviceId: string}){const{ streamUrl, streamToken, state, refetch }=useDeviceStream({ client, deviceId });return(<divstyle={{width: 360,height: 780}}><DeviceStreamstreamUrl={streamUrl}streamToken={streamToken}hasControlplaceholderLabel={state}onStreamHealed={refetch}/></div>);}Already have the device? Feed its fields straight in — no hook required:
constdevice=awaitclient.devices.retrieve(deviceId);<DeviceStreamstreamUrl={device.streamUrl}streamToken={device.streamToken}hasControl/>;The repo ships the playground shown above — paste an API key + device id, or a raw streamUrl + streamToken, and stream a real device:
bun install
bun run example # serves the playground at http://localhost:3000In SDK mode the example server exposes one narrow helper endpoint backed by @mobilerun/sdk so the API key can stay server-side while retrieving streamUrl and streamToken. The stream WebSocket itself is opened directly by the browser against streamUrl; the component appends streamToken as ?token=.
Import the prebuilt stylesheet once, anywhere:
import'@mobilerun/react/styles.css';It bundles the design tokens, only the Tailwind utilities the components use, the animations, and the structural CSS. It does not include Tailwind's preflight/reset, so it won't touch your base styles — and you don't need Tailwind installed.
Retheme by overriding the CSS variables on an ancestor (e.g. --background, --card, --muted-foreground, --border, --primary), or add a .dark ancestor for the bundled dark palette:
:root {
--primary:oklch(0.550.18268);
--background:oklch(0.9850.00485);
}bun install
bun run build # tsup (JS + d.ts) + Tailwind (styles.css) → dist/
bun run typecheck