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.”
Secure context and transient activation
Section titled “Secure context and transient activation”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.
Requesting and reusing a device
Section titled “Requesting and reusing a device”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.
Connect and disconnect events
Section titled “Connect and disconnect events”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.
Opening a device and exchanging reports
Section titled “Opening a device and exchanging reports”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.
Blocked reports
Section titled “Blocked reports”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).
Availability in workers
Section titled “Availability in workers”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.
Feature detection and fallback
Section titled “Feature detection and fallback”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;}Practical checklist
Section titled “Practical checklist”- Feature-detect
navigator.hidbefore 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.hidis exposed only in secure contexts. - Call
requestDevice()only with transient activation (a user gesture), or it rejects withSecurityError. - Call
getDevices()on later visits to reuse a previously authorized device without a new prompt. - Check
HIDDevice.opened/state before callingsendReport(),sendFeatureReport(), orreceiveFeatureReport()— each rejects withInvalidStateErroron 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
HIDDeviceobject.