Web NFC API Guide

Reading NFC tags in the browser

Web NFC API Guide

Web NFC brings NFC tag reading and writing directly to the browser — no native app, no app store, no install friction. Users tap a tag, the browser presents a permission prompt, and your web app receives the NDEF payload via a JavaScript API.

What Is the Web NFC API?

Web NFC is a W3C specification that exposes NFC hardware to web pages via the NDEFReader interface. It is part of the broader Project Fugu effort to close the gap between web and native capabilities on mobile platforms.

const reader = new NDEFReader();
await reader.scan();
reader.onreading = (event) => {
    const { serialNumber, message } = event;
    for (const record of message.records) {
        console.log("Record type:", record.recordType);
        console.log("MIME type:", record.mediaType);
        console.log("Data:", record.data);
    }
};

The NDEFReader handles both reading (scan()) and writing (write()). No separate writer class exists — the same object manages both operations.

Browser Support

Browser Platform Support Notes
Chrome 89+ Android Full Requires HTTPS + user gesture
Chrome for Android Android Full Foreground only
Safari iOS No Apple has not implemented Web NFC
Firefox All No Not planned
Samsung Internet Android 11.2+ Same engine as Chrome
Edge Android Yes Chromium-based

Web NFC is currently a Chrome on Android exclusive. iOS users must use a native app (Core NFC) or a PWA with a native bridge.

Reading Tags

async function readTag() {
    if (!('NDEFReader' in window)) {
        console.error('Web NFC not supported');
        return;
    }
    const reader = new NDEFReader();
    try {
        await reader.scan(); // triggers permission prompt on first use
        reader.addEventListener('reading', ({ message, serialNumber }) => {
            console.log('UID:', serialNumber);
            for (const record of message.records) {
                if (record.recordType === 'url') {
                    const decoder = new TextDecoder();
                    console.log('URL:', decoder.decode(record.data));
                }
            }
        });
        reader.addEventListener('readingerror', () => {
            console.error('Cannot read tag data');
        });
    } catch (err) {
        console.error('Error:', err);
    }
}

Writing Tags

async function writeUrl(url) {
    const writer = new NDEFReader();
    await writer.write({
        records: [{ recordType: 'url', data: url }]
    });
    console.log('Written successfully');
}

The write() method accepts an ndef-message object with an array of ndef-records. Supported recordType values: 'url', 'text', 'mime', 'smart-poster', 'absolute-url', 'empty', and custom external types.

Security Model

Web NFC enforces strict security constraints:

Constraint Reason
HTTPS only Prevents man-in-the-middle injection
User gesture required to start scan Blocks silent background scanning
Foreground document only No background service workers
Explicit permission per origin User controls which sites can read NFC
No access to tag UID by default Privacy — UID uniquely identifies tag

The permission model prevents nfc-enabled-device abuse: a rogue website cannot scan tags silently in the background. The serialNumber (UID) is only exposed during an active scan session that the user explicitly permitted.

Practical Patterns

Progressive enhancement: Feature-detect NDEFReader and fall back to a QR code scanner or manual input for unsupported browsers.

PWA integration: Combine Web NFC with a service worker and Web App Manifest for an installable, offline-capable tag management app — no app store required.

Use the NDEF Message Encoder to generate test payloads compatible with the Web NFC record format.

See also: Android NFC Programming Guide and How to Read and Write NFC Tags.

Terms in This Guide