Skip to main content

Documentation Index

Fetch the complete documentation index at: https://setup.despia.com/llms.txt

Use this file to discover all available pages before exploring further.

Communicate with physical NFC tags from your web app. The runtime activates the device’s NFC chip in either read or write mode, and the next tag the user taps against the phone is processed.
The device must have NFC hardware. All modern iPhones from XS onwards and most Android phones support NFC, but some budget Android models do not. On devices without NFC, the call resolves silently.

Installation

npm install despia-native
import despia from 'despia-native';

Setup

NFC on iOS is a capability that has to be enabled on the App ID itself before the runtime can access the NFC chip. Enable it in Apple Developer, toggle the addon in the Despia Editor, then rebuild.
1

Open Apple Developer

Sign in at developer.apple.com and go to Certificates, Identifiers & Profiles > Identifiers.
2

Open your core bundle ID

Select the App ID that matches your app’s core bundle ID (the one Despia builds against).
3

Enable NFC Tag Reading

Under Capabilities, tick NFC Tag Reading and save. Apple regenerates the provisioning profile with the new entitlement.
4

Enable NFC in the Despia Editor

Go to App > Addons and toggle NFC on.
5

Rebuild a fresh native binary

Trigger a new build from the Despia Editor. The new binary picks up the updated provisioning profile and the addon flag.
If you skip the rebuild, nfc://read and nfc://write resolve silently. No system NFC sheet appears, window.onNFCEvent never fires, and the call looks identical to running on a device without NFC hardware. Any change to the App ID capabilities or the Despia Editor addon list requires a fresh build to take effect.

How it works

NFC has two modes, read and write. Each mode is triggered by its own scheme call, and results flow back through a global event callback on window.
const isDespia = navigator.userAgent.toLowerCase().includes('despia')

// to read
if (isDespia) despia('nfc://read')

// to write
if (isDespia) despia(`nfc://write?value=${encodeURIComponent('hello')}`)
Each call is one-shot. The runtime enters the requested mode, processes a single tag, then exits. Trigger them independently from your own UI handlers, never both in the same gesture.

Handling NFC events

Define window.onNFCEvent once at app startup. The runtime invokes it with a single object whenever something happens during an active NFC session, whether a tag is read, a write completes, the user cancels, or anything goes wrong.
window.onNFCEvent = function (event) {
    switch (event.type) {
        case 'read':
            console.log('tag id:', event.id)
            console.log('payload:', event.data)
            break
        case 'write':
            console.log('write successful')
            break
        case 'dismissed':
            console.log('user cancelled the scan')
            break
        case 'error':
            console.error('NFC error:', event.error)
            break
    }
}
The four event types and their fields:
read
object
Fires when a tag is successfully scanned in read mode. Carries id, the tag’s unique identifier as a lowercase hex string (empty string if the device cannot read it), and data, the NDEF payload content as a string.
write
object
Fires when a write completes successfully. No additional fields.
dismissed
object
Fires when the user closes the system NFC sheet before a tag is scanned. No additional fields.
error
object
Fires on any other failure, read or write. Carries error, a human-readable message describing what went wrong.
A successful read or write does not also emit dismissed or error, the in-flight action is cleared as soon as the success event fires. You can treat the four event types as mutually exclusive per session.

Reading NFC tags

Call nfc://read to enter read mode. When the user taps a tag, the runtime fires window.onNFCEvent({ type: 'read', id, data }) with the tag identifier and the NDEF payload.
const isDespia = navigator.userAgent.toLowerCase().includes('despia')

window.onNFCEvent = function (event) {
    if (event.type === 'read') {
        console.log('tag id:', event.id)
        console.log('payload:', event.data)
    }
}
Trigger the read from any UI element:
<button onclick="despia('nfc://read')">Scan a tag</button>

Writing to NFC tags

Pass the value to write as the value query parameter. The runtime enters write mode, the next tag the user taps receives the payload, and window.onNFCEvent({ type: 'write' }) fires when the write completes.
const isDespia = navigator.userAgent.toLowerCase().includes('despia')

window.onNFCEvent = function (event) {
    if (event.type === 'write') {
        console.log('write successful')
    }
}

const value = 'https://example.com'
if (isDespia) despia(`nfc://write?value=${encodeURIComponent(value)}`)
Always wrap the value in encodeURIComponent. Without it, URLs with ?, &, or = inside the payload will break the scheme parse on the native side. Trigger the write from any UI element:
<button onclick="despia(`nfc://write?value=${encodeURIComponent('https://example.com')}`)">Write URL to tag</button>

Handling cancellation and failure

The runtime distinguishes between the user closing the NFC sheet and an actual failure. A dismissed event fires when the user backs out before a tag is scanned, and is not an error condition. An error event fires for everything else, including malformed tags, tags that are not NDEF formatted, write failures, and session timeouts.
window.onNFCEvent = function (event) {
    if (event.type === 'dismissed') {
        // user closed the sheet, reset UI without surfacing a failure
        resetScanButton()
        return
    }

    if (event.type === 'error') {
        // surface a retry prompt, log the underlying message
        showToast(`NFC failed: ${event.error}`)
    }
}
The error message is a passthrough of the underlying platform error string, suitable for logging or developer-facing diagnostics. For end-user messaging, map the type to your own copy rather than displaying the raw string.

Writing a vCard

To write a contact card that a phone can save directly from a tap, format the payload as a vCard string prefixed with VCARD_ and join the properties with underscores.
const contact = {
    name: 'John Doe',
    title: 'Developer',
    phone: '0452641211'
}

const vcard = `VCARD_FN:${contact.name}_Title:${contact.title}_TEL;TYPE=CELL:${contact.phone}`

if (isDespia) despia(`nfc://write?value=${encodeURIComponent(vcard)}`)
Any standard vCard property works (FN, ORG, EMAIL, URL, ADR, TEL, and the rest). When another phone reads the tag, the OS recognises the vCard and offers to save the contact natively.

Resources

NPM Package

despia-native