# Web NFC: reading and writing NFC tags from the web

> How NDEFReader.scan() and write() exchange NDEF messages with NFC tags, the spec's secure-context and visible-top-level-document requirements, the "nfc" permission prompt, and the current non-Baseline browser support starting at Chrome for Android 89.

**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."

## What it exchanges

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.

## Reading and writing with NDEFReader

```js
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."

## Secure context and document visibility

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."

## Permission model

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.

## Feature detection and fallback

```js
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;
}
```

## Where it is supported

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.

## Practical checklist

- [ ] 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.

## Cross-references

- [WebHID API](/reference/capabilities/web-hid/)
- [Web Bluetooth](/reference/capabilities/web-bluetooth/)