npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-web-serial-api

v0.2.0

Published

W3C Web Serial API (navigator.serial) for React Native on Android, backed by a USB-serial TurboModule (built on mik3y/usb-serial-for-android).

Readme

react-native-web-serial-api

The W3C Web Serial API (navigator.serial) for React Native on Android, backed by a USB-serial TurboModule built on top of mik3y/usb-serial-for-android.

Use the same API you already know from the browser - serial.requestPort(), port.open(), port.readable, port.writable, getPorts(), setSignals(), and getSignals() - in a React Native app that talks to USB serial devices.

At a glance

  • Spec-style Serial / SerialPort implementation
  • New Architecture TurboModule
  • Native port picker and Android USB permission handling
  • Web Streams under the hood (ReadableStream / WritableStream)
  • Works with browser-style code on web by delegating to the native navigator.serial

Platform support

| Platform | Support | Notes | | --- | --- | --- | | Android | Yes | Native USB-serial support through the TurboModule. | | Web | Yes | Delegates to the browser's native navigator.serial. | | iOS | No | Generic USB-serial access is not available, so autolinking is disabled. |

Quick start

npm install react-native-web-serial-api
# or
yarn add react-native-web-serial-api

This is a New Architecture library. Make sure your app has the New Architecture enabled. No manual linking is required - the module is autolinked.

Minimal usage

import {serial} from 'react-native-web-serial-api';

async function run() {
  // Must be called from a user gesture on web.
  const port = await serial.requestPort({
    filters: [{usbVendorId: 0x0403}], // optional, for example FTDI only
  });

  await port.open({baudRate: 115200, dataBits: 8, stopBits: 1, parity: 'none'});

  const writer = port.writable.getWriter();
  await writer.write(new TextEncoder().encode('Hello\n'));
  writer.releaseLock();

  const reader = port.readable.getReader();
  const {value} = await reader.read();
  console.log(value);
  reader.releaseLock();

  await port.close();
}

Android setup

The library ships its own AndroidManifest.xml that declares the port picker activity, the detach receiver, and the android.hardware.usb.host feature, so usually no extra configuration is needed.

If you want your app to launch automatically when a matching device is plugged in, add an intent filter to your launcher activity in android/app/src/main/AndroidManifest.xml:

<activity android:name=".MainActivity" ...>
  <intent-filter>
    <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
  </intent-filter>
  <meta-data
    android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
    android:resource="@xml/device_filter" />
</activity>

The bundled @xml/device_filter matches common USB-serial chips:

  • CDC-ACM
  • FTDI 0x0403
  • CP210x 0x10C4
  • CH34x 0x1A86
  • PL2303 0x067B

Provide your own res/xml/device_filter.xml to override it.

Which API should I use?

| Use case | Start with | Why | | --- | --- | --- | | Real hardware in your app | Serial / SerialPort | The browser-style Web Serial API you already know. | | Quick in-memory smoke tests | InMemorySerialTransport + LoopbackDevice | Fastest way to exercise bytes without hardware. | | Protocol tests against a simulated peripheral | SimulatedDevice + createDeviceFixture + SerialClient | Gives you both sides of the conversation in one test. | | App-to-app or emulator-to-host testing | exposeSimulatedDevice + WebSocketSerialTransport | Runs the same simulated device behind a real WebSocket bridge. |

Core concepts

serial

The package exports a ready-to-use serial singleton, which is the equivalent of navigator.serial.

import {serial} from 'react-native-web-serial-api';

const ports = await serial.getPorts();

Permissions

There are two different permission concepts:

  • serial.requestPort() is the Web Serial permission flow. In Android mode it shows the native picker and requests Android USB permission for the selected device.
  • Android USB permission can also be granted outside the app, for example through the system attach dialog or an USB_DEVICE_ATTACHED intent filter.

On Android, serial.getPorts() returns the devices the app can currently access through Android USB permission, regardless of how that permission was obtained. On web, getPorts() returns ports previously granted by the site in the browser's persistent permission store.

const ports = await serial.getPorts();
const port = await serial.requestPort();

Connect and disconnect events

serial.addEventListener('connect', () => console.log('device attached'));
serial.addEventListener('disconnect', () => console.log('device detached'));

port.addEventListener('disconnect', () => console.log('this port went away'));

On Android, serial fires connect when a USB device is attached and when the app is granted USB permission for a device. A common pattern is to re-run getPorts() on every connect / disconnect so your device list stays fresh.

Control and status signals

await port.setSignals({dataTerminalReady: true, requestToSend: false});
const {clearToSend, dataCarrierDetect, ringIndicator, dataSetReady} =
  await port.getSignals();

Browser-only option note

In Android USB mode, allowedBluetoothServiceClassIds is not supported by requestPort() and will throw a TypeError if provided.

API reference

The package exposes:

| Export | Description | | --- | --- | | serial | A ready-to-use Serial instance. | | Serial, SerialPort | The Web Serial API classes. | | UsbSerial | Lower-level access to the raw USB-serial TurboModule (Android only). | | Event, EventTarget | Polyfill implementations used only when the runtime does not already provide these globals. | | Types | SerialOptions, SerialOutputSignals, SerialInputSignals, SerialPortInfo, SerialPortFilter, SerialPortRequestOptions. |

Example app

The example/ app is a React Native port of SimpleUsbTerminal built on this package's Web Serial API. It includes:

  • a Devices screen with baud-rate selection
  • a Terminal screen with colored send / receive logs
  • HEX mode
  • newline selection
  • clear
  • control lines with RTS / DTR toggles
  • flow control
  • Send BREAK

Run the example on Android

npm install
cd example
npm install
npm run android

Because the example uses the Web Serial API, a few details differ from SimpleUsbTerminal:

  • Driver / chip name is not exposed by the Web Serial API. Rows show Vendor/Product plus a best-effort chip label from known vendor IDs.
  • Flow control is limited to None and Hardware (RTS-CTS). XON/XOFF and DTR/DSR are not in the Web Serial spec. Changing it reconnects the port.
  • The Android foreground-service notification is omitted because it is service plumbing unrelated to serial I/O.

Run the example in the browser

The example also runs as a web app via react-native-web and Vite. On web, the package delegates to the browser's native navigator.serial, so the same App.tsx talks to real serial hardware through browser permissions.

cd example
npm install
npm run web
# npm run web:build

Web Serial works in Chromium-based browsers over a secure context. http://localhost counts. requestPort() must be called from a user gesture, and the example's Request port button handles that.

Testing and simulation

The transport layer lets you test without USB hardware, whether you want a quick loopback check, a stateful simulated peripheral, or a full WebSocket E2E path. The example app also includes a Self Test screen and a Virtual device (demo) mode.

Fast in-memory test

import {Serial} from 'react-native-web-serial-api';
import {
  InMemorySerialTransport,
  LoopbackDevice,
} from 'react-native-web-serial-api/testing';

const transport = new InMemorySerialTransport();
transport.addDevice(
  new LoopbackDevice({usbVendorId: 0x0403, usbProductId: 0x6001}),
  {hasPermission: true},
);

const serial = new Serial(transport);

Host-side protocol test

import {createDeviceFixture, SimulatedDevice} from 'react-native-web-serial-api/testing';

class Thermometer extends SimulatedDevice {
  readonly usbVendorId = 0x10c4;
  readonly usbProductId = 0xea60;

  onOpen() {
    this.send('READY\r\n');
  }

  emitTemperature(value: number) {
    this.send(`temp=${value}\r\n`);
  }
}

const {client, simulatedDevice, whenOpened} =
  await createDeviceFixture(new Thermometer());

await client.open({baudRate: 115200});
await whenOpened();
simulatedDevice.emitTemperature(21.5);
expect(await client.readLine()).toBe('temp=21.5');
await client.close();

For the full guide to SerialClient, createDeviceFixture, fault injection, runTestSuite, compareTestResults, exposeSimulatedDevice, and the conformance suites, see TESTING.md.

Remote serial over WebSocket

You can drive a real serial port plugged into another machine. This is useful for developing in a Chromium browser or an Android emulator that cannot see the USB device directly, or for remote debugging.

On the host machine, run the bundled CLI:

npx -p react-native-web-serial-api expose-serial-websocket \
  --port /dev/ttyUSB0 --baudrate 115200
# add --allow-remote to bind 0.0.0.0

In the app:

import {Serial} from 'react-native-web-serial-api';
import {WebSocketSerialTransport} from 'react-native-web-serial-api/websocket';

const serial = new Serial(new WebSocketSerialTransport('ws://localhost:8080'));
const [port] = await serial.getPorts();
await port.open({baudRate: 115200});
const writer = port.writable!.getWriter();
await writer.write(new TextEncoder().encode('Hello serial!\n'));

The example app includes this under Devices -> menu -> Remote serial (WebSocket).

The WebSocket carries raw serial bytes as binary frames and a small JSON control protocol as text frames (setLineCoding, setSignals / getSignals, startReading / stopReading, flush, break, and so on). By default the bridge binds to localhost. Use --allow-remote only on trusted networks.

Troubleshooting

  • If Android never shows your device, confirm USB host support and the device filter.
  • If requestPort() does nothing on web, make sure it is called from a button tap or other user gesture.
  • If the app works on web but not Android, check that New Architecture is enabled and the device has Android USB permission.
  • If getPorts() is empty on Android, unplug and replug the device, then grant permission again if Android revoked it.

How it works

The JavaScript layer in src/ implements the Web Serial API on top of a thin TurboModule (NativeUsbSerial) whose native Android implementation in android/src/main/java/dev/webserialapi/ wraps usb-serial-for-android. Reads and writes are bridged to ReadableStream / WritableStream via web-streams-polyfill when the runtime does not already provide Web Streams globals.

License

MIT © Aras Abbasi