Skip to content

Capabilities · API

WebHID API: navigator.hid device access from the web

Published

In one line: WebHID lets a web page request access to a Human Interface Device (HID) — “a type of device that takes input from or provides output to humans,” using the HID protocol originally built for USB and since implemented over other transports including Bluetooth — through a permission prompt. MDN marks it experimental and not Baseline, “because it does not work in some of the most widely-used browsers.”

navigator.hid is exposed only in secure contexts: both the Navigator and WorkerNavigator partial interfaces that add it are annotated [SecureContext] in the spec. Calling requestDevice() also requires transient activation — the spec rejects the returned promise with a SecurityError DOMException if “the relevant global object of this does not have transient activation,” and rejects with NotSupportedError if the caller is not a window context.

const devices = await navigator.hid.requestDevice({ filters: [] });

requestDevice() shows a permission prompt listing candidate devices (an empty filters array matches everything); the user selects a device and clicks Connect. Once a device has been authorized, getDevices() returns it again without prompting:

const devices = await navigator.hid.getDevices();
devices.forEach((device) => {
console.log(`HID: ${device.productName}`);
});

Access to both methods is also gated by a Permissions Policy feature named "hid": the spec rejects with SecurityError if the calling document is not “allowed to use” it.

navigator.hid.addEventListener("disconnect", (event) => {
console.log(`HID disconnected: ${event.device.productName}`);
});

The HID interface fires matching connect and disconnect events, each carrying an HIDConnectionEvent with a device property, when a previously authorized device is attached or removed.

HIDDevice.open() rejects with InvalidStateError unless the device’s state is "closed"; otherwise it asks the OS to open the device and resolves once open (or rejects with NetworkError on failure). close() rejects pending report promises with AbortError, closes the OS handle, and returns the device to the "closed" state. sendReport(), sendFeatureReport(), and receiveFeatureReport() each reject with InvalidStateError if the device is not "opened", and with TypeError if the report ID does not match whether the device’s interface uses report IDs.

The spec defines a “blocked report” concept: sendReport(), sendFeatureReport(), and receiveFeatureReport() each check whether the target report is blocked and, if so, reject with NotAllowedError; a blocked input report simply does not fire an inputreport event. The spec separately describes device- and usage-based blocking — for example, blocking by vendor/product ID or by inspecting the HID usage values assigned to a device’s collections (the spec gives keyboards as an illustrative example of usage-based blocking).

MDN notes this feature is available in Web Workers, “except for Shared Web Workers” — WorkerNavigator.hid exposes it in dedicated workers, alongside Navigator.hid on the main thread.

async function connectHidDevice() {
if (!("hid" in navigator)) {
// WebHID is unsupported here — fall back to a manual pairing UI
// or another transport instead of calling navigator.hid.
return null;
}
const [device] = await navigator.hid.requestDevice({ filters: [] });
return device ?? null;
}
  • Feature-detect navigator.hid before calling any WebHID method — it is experimental and not Baseline per MDN, so absence is expected in some browsers.
  • Serve the page over HTTPS; navigator.hid is exposed only in secure contexts.
  • Call requestDevice() only with transient activation (a user gesture), or it rejects with SecurityError.
  • Call getDevices() on later visits to reuse a previously authorized device without a new prompt.
  • Check HIDDevice.opened/state before calling sendReport(), sendFeatureReport(), or receiveFeatureReport() — each rejects with InvalidStateError on a device that is not open.
  • Do not assume a report will succeed just because the device is open: the spec allows a “blocked report” on an open device to be rejected with NotAllowedError.
  • Remember one physical device can be represented by more than one HIDDevice object.

Specifications

SpecificationStatus
None.