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 self-healing reconnects. - 🪝
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 self-heals:
⚠️ 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.
import Mobilerun from '@mobilerun/sdk';
import { DeviceStream, useDeviceStream } from '@mobilerun/react';
import '@mobilerun/react/styles.css';
const client = new Mobilerun({ apiKey: process.env.MOBILERUN_API_KEY });
export function DevicePanel({ deviceId }: { deviceId: string }) {
const { streamUrl, streamToken, state, refetch } = useDeviceStream({ client, deviceId });
return (
<div style={{ width: 360, height: 780 }}>
<DeviceStream
streamUrl={streamUrl}
streamToken={streamToken}
hasControl
placeholderLabel={state}
onStreamHealed={refetch}
/>
</div>
);
}Already have the device? Feed its fields straight in — no hook required:
const device = await client.devices.retrieve(deviceId);
<DeviceStream streamUrl={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.55 0.18 268);
--background: oklch(0.985 0.004 85);
}bun install
bun run build # tsup (JS + d.ts) + Tailwind (styles.css) → dist/
bun run typecheck