Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md

Expo Device Hub

expo-device-hub

Expo Device Hub is an Expo DevTools plugin that lets you preview and control your iOS simulators and Android emulators right from the browser — without leaving your development workflow. When you run expo start, the Hub adds a device dashboard where you can watch a live stream of any device, interact with it, and manage which devices are running from one place.

Features

  • Live stream of iOS simulators and Android emulators in your browser.
  • Interact directly — tap, swipe, scroll, and type into the device.
  • Boot, shut down, and add devices without opening Xcode or Android Studio.
  • Follows your system light/dark theme, and can flip the device's appearance too.
  • Feed an Android emulator's camera a PNG from the inspector's Camera section.

iOS simulators require macOS with Xcode. Android emulators require the Android SDK (emulator, adb).

Use in an Expo app

Using the Hub inside an Expo app requires Expo SDK 57 or newer.

Install the plugin:

npx expo install expo-device-hub

Then start your project as usual:

npx expo start

Expo Device Hub registers itself as a DevTools plugin, so a link to it appears in your terminal when the dev server starts:

› Expo Device Hub: http://localhost:8081/_expo/plugins/expo-device-hub

Use standalone

The Hub also runs outside of expo start as a standalone server — useful when you want the device dashboard without a running Expo project:

npx expo-device-hub

Screenshots in EAS sessions

Set EXPO_DEVICE_HUB_SCREENSHOT_DIRECTORY to save each preview screenshot as a file. The browser download still works as before. Both serve-sim and serve-emu write each captured PNG to a temporary file in that directory, then rename it. A saved file has a name such as screenshot-2026-09-24T08-45-59-123Z-a1b2c3d4e5f6.png. The name contains the UTC capture time and a random suffix. If the save fails, serve-sim or serve-emu logs the error to stderr and writes a screenshot-<time>-<suffix>.failed.json record with the file name, error, and time into the same directory. The EAS worker reports that record. The screenshot response still returns the PNG. Each screenshot response carries the save outcome in the X-Expo-Screenshot-Artifact header. When the save fails, the serve-sim preview and the Device Hub dashboard show a notice after the download. On iOS, serve-sim records each manual screenshot as a session event in the Events panel. A failed save appears there as an error event with the reason. The reason sent to the browser is only the error code, such as ENOSPC, or unknown error. The full error, with the path, goes only to the stderr line and the .failed.json record. The EAS worker reports that record in the job log. A serve-sim preview opened on a loopback hostname keeps its Desktop save and does not use the directory. Standalone previews without the environment variable keep their existing behavior.

The EAS worker in eas-cli build-tools sets the variable and uploads completed files. Upload naming and retry behavior are documented in deviceRunSessionScreenshots.ts.

Repository structure

This is a Bun workspace orchestrated with Turborepo.

PackageWhat it is
packages/expo-device-hubThe main DevTools plugin.
packages/@expo/hub-clientDevice-client hooks and types that own the connection to serve-sim / serve-emu and paint the live stream. See hub-client below.
packages/@expo/hub-componentsDependency-free UI kit (Sidebar, StreamPanel, Button, …) built on @expo/styleguide design tokens, so Hub matches the Expo dashboard website.
packages/@expo/hub-apple-utilsLists, creates, and boots Apple simulator devices via simctl (macOS only).
packages/@expo/hub-android-utilsLists, creates, and boots Android emulators via avdmanager / sdkmanager / emulator.
packages/serve-simSource for @expo/serve-sim, maintained and published from this monorepo.
packages/serve-emuSource for the serve-emu workspace package, maintained in this monorepo.
exampleA minimal Expo app with the plugin installed.

Getting started

Install dependencies with Bun 1.3.14 and build every package once from the repo root:

bun install
bun run build   # turbo build across all packages

Formatting

JavaScript and TypeScript use Oxfmt with double quotes, including JSX attributes, as configured in .oxfmtrc.json. Oxfmt may choose the other quote when it avoids escaping. bun run format formats all supported repository files, and bun run lint checks their formatting before running the workspace linters. The existing EAS Checks job runs the same lint command. Installed agent skills and LLP tooling are excluded so their upstream copies and receipts stay intact.

Run the example

The example app is a host Expo project that has expo-device-hub installed as a DevTools plugin. Use it to see the Hub exactly as an end user would.

cd example
bun start       # or: bun run ios / bun run android / bun run web

Develop

To iterate on the dashboard UI with Metro fast refresh, run expo-device-hub as a standalone Expo web app:

cd packages/expo-device-hub
bun start          # expo start --web, on port 8081

This serves the Dashboard component directly, so edits to the UI hot-reload without going through a host app. A local "inception" DevTools module (modules/expo-device-hub) registers the plugin against itself, so the standalone app still gets the real /api/devices backend while you develop.

The device server is bundled (see scripts/build-plugin-server.ts), so hot reload covers the UI. After changing anything under src/server, rebuild it with bun run build:server.

CI and Xcode support

The published packages build serve-sim's native code with Xcode 26, so they work for users on Xcode 26 and Xcode 27. LLP 0014 has the full support statement.

WhenWhat runs
Every push to a PRChecks: lint and typecheck
Every push to a PR that is ready for reviewserve-sim tests: the Xcode 26 build on the iOS 26 and iOS 27 Simulators
Every push to a draft PR with the ci-ready labelserve-sim tests, as above
Every push to a PR with the xcode-27 labelAlso an Xcode 27 build, tested on the iOS 27 Simulator
Every night on mainThe Xcode 27 build, tested on the iOS 27 Simulator

The serve-sim tests run only when a PR changes serve-sim or its CI files, and they use several macOS machines. Open a PR as a draft while you iterate, and add ci-ready only when you want a run.

hub-client

@expo/hub-client is the device-client layer. The two backends speak very different wire protocols — serve-sim streams MJPEG/H.264 and takes binary touch packets, while serve-emu streams H.264 (WebCodecs) and takes JSON gestures — so this package hides that behind one shared contract:

  • DeviceClientProvider to share one connection, with useDeviceClient and useDeviceClientSelector to read state and controls,
  • deviceSettingsStatus to report device-options loading and availability independently of video and app activity,
  • a DeviceScreen component that paints whichever stream is active and forwards pointer/gesture/keyboard/scroll-wheel input, and
  • a KeyboardCapture component (with useCoarsePointer) that lets touch clients type into the device with their phone keyboard via client.sendKeyEvents.

The serve-sim side tracks the @expo/serve-sim web client: host work (simulator settings, app-bundle details) goes through its typed exec-ws actions rather than shell commands. The client is written against the server in packages/serve-sim, in this monorepo.

It lives in its own package (rather than inside the plugin) so the Expo dashboard website can consume the exact same code to mirror devices in the browser. It is published to npm as @expo/hub-client; see the package README for install and usage examples.

关于 About

A single control surface for every simulator, emulator, and device to develop your Expo app.
androidemulatorexpoiossimulator

语言 Languages

TypeScript87.3%
Swift8.4%
Objective-C2.3%
C0.5%
Python0.4%
CSS0.3%
Shell0.3%
HTML0.2%
JavaScript0.1%
Assembly0.0%

提交活跃度 Commit Activity

代码提交热力图
过去 52 周的开发活跃度
413
Total Commits
峰值: 55次/周
Less
More

核心贡献者 Contributors