use-serial
Talk to serial devices with the Web Serial API.
| Package | @vizejs/composable/use-serial |
| Own the source | vize lib pull composable:use-serial |
| Runtime exports | useSerial |
| Gzip budget | 2560 B |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/composable
Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.
import { useSerial } from "@vizejs/composable/use-serial";
Try it
Read a microcontroller's log
Open a serial port and stream decoded text lines from a connected board.
What to observe: Connect a board opens the port chooser where Web Serial exists; incoming lines appear in the output area.
decode: "text" decodes chunks as UTF-8 and onData receives each chunk. Set the baud rate your board uses. Every example renders a neutral message until it mounts, so server and client markup match, and then explains what to do when the browser lacks the API.
Compare the initial and interacted states


Copy the example
Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.
<script setup lang="ts">
import { ref } from "vue";
import { useMounted } from "@vizejs/composable/use-mounted";
import { useSerial } from "@vizejs/composable/use-serial";
const mounted = useMounted();
const lines = ref<string[]>([]);
const { supported, connected, requestPort, open, close } = useSerial({
decode: "text",
onData: (chunk) => {
lines.value = [...lines.value, String(chunk)].slice(-5);
},
});
async function connectBoard() {
const port = await requestPort();
if (port) await open(port, { baudRate: 115200 });
}
</script>
<template>
<div class="composable-example">
<p>Read log lines from a microcontroller over a serial port.</p>
<div class="example-actions">
<button type="button" :disabled="!mounted || !supported || connected" @click="connectBoard">
Connect a board
</button>
<button type="button" :disabled="!connected" @click="() => close()">Disconnect</button>
</div>
<pre aria-label="Serial output">{{ lines.join("\n") }}</pre>
<output aria-live="polite">{{
!mounted
? "Checking browser support…"
: supported
? connected
? "Reading from the board."
: "Ready to connect."
: "Web Serial is not available in this browser."
}}</output>
</div>
</template>
Runtime contract
| Utility | Category | Stability | SSR | Hydration | Cleanup | Targets | Host globals | Uses |
|---|---|---|---|---|---|---|---|---|
useSerial |
system | experimental | deterministic-fallback | caller-managed | reactive-scope, returned-value | web, desktop | window |
tryOnScopeDispose |
API
useSerial
Talk to serial devices with the Web Serial API. requestPort() prompts for a port, open() opens it and starts a read loop that feeds onData with bytes or decoded text, write() sends text or bytes. ports follows the host's connect / disconnect events and a port's own disconnect closes the session. Failures land in error. The read loop is cancelled and the port closed when the owning reactive scope stops; outside a scope call close(). Server rendering: supported and connected are false, nothing is read.
function useSerial<const Decoding extends SerialDecoding = "bytes">( options: UseSerialOptions<Decoding> = {}, ): SerialControls
const serial = useSerial({ decode: "text", onData: (text) => log(text) });
const port = await serial.requestPort([{ usbVendorId: 0x2341 }]);
if (port) await serial.open(port, { baudRate: 115200 });
Types
SerialPortFilter
USB identifiers used to filter requestPort.
| Member | Type | Description |
|---|---|---|
usbVendorId? |
number |
USB vendor id. |
usbProductId? |
number |
USB product id. |
bluetoothServiceClassId? |
number | string |
Bluetooth RFCOMM service class id. |
SerialOpenOptions
Line settings passed to SerialPort.open.
| Member | Type | Description |
|---|---|---|
baudRate |
number |
Baud rate, e.g. 9600 or 115200. |
dataBits? |
number |
Data bits per frame (7 or 8). |
stopBits? |
number |
Stop bits (1 or 2). |
parity? |
"none" | "even" | "odd" |
Parity mode. |
bufferSize? |
number |
Read/write buffer size in bytes. |
flowControl? |
"none" | "hardware" |
Flow control mode. |
SerialPortLike
Minimal SerialPort used by useSerial.
| Member | Type | Description |
|---|---|---|
readable |
ReadableStream<Uint8Array> | null |
Byte stream from the device while open; null otherwise or after a fatal error. |
writable |
WritableStream<Uint8Array> | null |
Byte sink to the device while open. |
open |
(options: SerialOpenOptions) => Promise<void> |
Open the port. |
close |
() => Promise<void> |
Close the port. |
SerialHost
navigator.serial-like capability used by useSerial.
| Member | Type | Description |
|---|---|---|
getPorts |
() => Promise<SerialPortLike[]> |
Ports the page already has access to. |
requestPort |
(options?: { readonly filters?: readonly SerialPortFilter[]; }) => Promise<SerialPortLike> |
Prompt the user for a port. |
UseSerialOptions
Options for useSerial.
| Member | Type | Description |
|---|---|---|
host? |
MaybeRefOrGetter<SerialHost | null | undefined> |
navigator.serial-like capability. |
decode? |
Decoding |
Deliver raw bytes or text decoded with encoding (streaming, so split multi-byte characters are joined). |
encoding? |
string |
Text encoding used when decode is "text". |
onData? |
(chunk: SerialChunk<Decoding>) => void |
Receives every chunk read from the open port. |
SerialControls
Reactive state and actions returned by useSerial.
| Member | Type | Description |
|---|---|---|
supported |
ComputedRef<boolean> |
Whether Web Serial is available. |
ports |
Readonly<ShallowRef<readonly SerialPortLike[]>> |
Ports the page has access to, refreshed on connect / disconnect. |
port |
Readonly<ShallowRef<SerialPortLike | undefined>> |
Currently open port. |
connected |
Readonly<Ref<boolean>> |
Whether a port is open. |
error |
Readonly<ShallowRef<unknown>> |
Most recent failure (request, open, read, write). |
requestPort |
( filters?: readonly SerialPortFilter[], ) => Promise<SerialPortLike | undefined> |
Prompt the user for a port (requires user activation). |
getPorts |
() => Promise<readonly SerialPortLike[]> |
Refresh ports. |
open |
(port: SerialPortLike, options: SerialOpenOptions) => Promise<boolean> |
Open port (closing any other open port) and start the read loop. |
write |
(data: string | Uint8Array) => Promise<boolean> |
Write text (UTF-8 encoded) or bytes. |
close |
() => Promise<void> |
Stop reading and close the open port. Idempotent. |