Skip to content

Capabilities · API

Web NFC: reading and writing NFC tags from the web

Published Updated

In one line: “The Web NFC API allows exchanging data over NFC via light-weight NFC Data Exchange Format (NDEF) messages,” per MDN. MDN marks it experimental and notes it “is not Baseline because it does not work in some of the most widely-used browsers.”

MDN notes that “devices and tags have to be formatted and recorded specifically to support NDEF record format to be used with Web NFC,” and that “low-level operations are currently not supported by the API” (though a public discussion about adding such functionality exists). MDN’s Web NFC API overview lists three interfaces: NDEFMessage (“represents NDEF messages that can be received from or sent to a compatible tag”), NDEFReader (“enables reading and writing messages from compatible NFC tags”), and NDEFRecord (“represents NDEF records that can be included in an NDEF message”). The W3C-CG specification additionally defines an NDEFReadingEvent interface, used for the reading event described below.

const ndef = new NDEFReader();
await ndef.scan();
ndef.onreading = (event) => {
console.log(`NFC tag read: ${event.serialNumber}`);
};

Per MDN, NDEFReader.scan() “activates a reading device and returns a Promise that either resolves when an NFC tag read operation is scheduled or rejects if a hardware or permission error is encountered,” and “triggers a permission prompt if the nfc permission has not been previously granted.” NDEFReader.write() behaves the same way for writing: it “attempts to write an NDEF message to a tag” and also triggers the nfc permission prompt if not already granted. The interface fires a reading event “when a new reading is available from compatible NFC devices,” and a readingerror event “when a tag is in proximity of a reading device, but cannot be read.”

The Web NFC specification’s security policies state plainly: “Only secure contexts are allowed to access NFC content” — “Browsers MAY ignore this rule for development purposes only.” The spec also restricts access to the foreground: “Web NFC functionality is allowed only for the Document of the top-level browsing context, where its visibilityState is 'visible',” and “for backgrounded web pages, receiving and writing NFC content must be suspended.”

The spec defines Web NFC as “a default powerful feature which is identified by the powerful feature name nfc,” gated behind an “obtaining permission” algorithm that checks the current permission state for that feature before allowing access.

async function scanNfcTags(onTag) {
if (!("NDEFReader" in window)) {
// Web NFC unsupported here — fall back to manual entry or a QR code instead.
return false;
}
const ndef = new NDEFReader();
await ndef.scan();
ndef.onreading = onTag;
return true;
}

Per MDN browser-compat data, NDEFReader support starts at Chrome for Android, version 89, with WebView for Android and other Chromium-derived Android browsers in MDN’s data mirroring that same Chrome-for-Android support level. Desktop Chrome, Firefox, and Safari all report no support (version_added: false). This matches MDN’s “Limited availability” / “not Baseline” labeling on the Web NFC API overview page.

  • Feature-detect "NDEFReader" in window before use — MDN marks Web NFC experimental and not Baseline, and per BCD its support starts at Chrome for Android 89+.
  • Serve the page over HTTPS: the spec allows access “only” in secure contexts (with a documented dev-only exception).
  • Do not expect reads or writes to continue once the page is backgrounded — the spec requires suspending NFC access when the top-level document is not visible.
  • Expect a permission prompt from scan() or write() whenever the nfc permission has not already been granted, per MDN; handle the rejected promise for a hardware or permission error.
  • Read MDN’s general note that devices and tags “have to be formatted and recorded specifically to support NDEF record format to be used with Web NFC” — but note the spec separately lists “writing to a non-formatted NFC tag” among its rudimentary interaction examples, so do not assume every tag needs pre-existing NDEF formatting before a write.

Specifications

SpecificationStatus
None.