React Native NFC Guide

NFC in JavaScript with react-native-nfc-manager

| 4 min read

React Native NFC Guide: react-native-nfc-manager

react-native-nfc-manager by revtel-tech is the most widely deployed NFC library for React Native, supporting Android, iOS, and partial HarmonyOS targets. It exposes NFC operations as an async JavaScript API, handles permission flows on both platforms, and provides low-level tech access for MIFARE and ISO 7816 APDU operations.

Installation

npm install react-native-nfc-manager
# or
yarn add react-native-nfc-manager

iOS: Run cd ios && pod install. Add to Info.plist:

<key>NFCReaderUsageDescription</key>
<string>Read and write NFC tags.</string>

Add Near Field Communication Tag Reading capability in Xcode Signing & Capabilities. Add the entitlement com.apple.developer.nfc.readersession.formats = NDEF.

Android: Permissions are declared automatically via the library's manifest merger. Confirm android.permission.NFC appears in your merged manifest. Set minSdkVersion to at least 16.

Initialisation

Call start() once at app startup (e.g. in App.tsx):

import NfcManager, { NfcTech } from 'react-native-nfc-manager';

// In your root component or a singleton service
NfcManager.start();

Check support before presenting any NFC UI:

const supported = await NfcManager.isSupported();
const enabled  = await NfcManager.isEnabled(); // Android only

Reading NDEF Tags

import NfcManager, { NfcTech, Ndef } from 'react-native-nfc-manager';

async function readNdef(): Promise<string | null> {
  try {
    await NfcManager.requestTechnology(NfcTech.Ndef);
    const tag = await NfcManager.getTag();
    if (!tag?.ndefMessage?.length) return null;

    const record = tag.ndefMessage[0];
    // Decode URI record
    const uri = Ndef.uri.decodePayload(new Uint8Array(record.payload));
    return uri;
  } catch (err) {
    console.warn('NFC read error:', err);
    return null;
  } finally {
    NfcManager.cancelTechnologyRequest();
  }
}

requestTechnology() shows the iOS NFC sheet and waits for a tag on Android. Always call cancelTechnologyRequest() in the finally block — a leaked session prevents future scans.

Writing NDEF Tags

async function writeUrl(url: string): Promise<void> {
  try {
    await NfcManager.requestTechnology(NfcTech.Ndef);
    const bytes = Ndef.encodeMessage([Ndef.uriRecord(url)]);
    if (bytes) {
      await NfcManager.ndefHandler.writeNdefMessage(bytes);
    }
  } finally {
    NfcManager.cancelTechnologyRequest();
  }
}

Verify the payload fits within user-memory using the Memory Calculator before writing. A write to an undersized tag will throw TagLostException or silently truncate.

Parsing Records Manually

The Ndef utility covers URI and Text. For raw ndef-record inspection:

import { NdefRecord } from 'react-native-nfc-manager';

function parseRecord(record: NdefRecord) {
  const { tnf, type, id, payload } = record;
  // tnf = 1 (NFC Forum Well-Known), type = [0x55] → URI
  if (tnf === 1 && type[0] === 0x55) {
    const prefix = uriPrefixMap[payload[0]] ?? '';
    return { kind: 'uri', value: prefix + String.fromCharCode(...payload.slice(1)) };
  }
  // tnf = 2 → MIME type
  if (tnf === 2) {
    const mimeType = String.fromCharCode(...type);
    return { kind: 'mime', mimeType, payload };
  }
  return { kind: 'unknown', tnf, type };
}

Verify your parsing against the NDEF Decoder for any edge-case payloads.

ISO 14443-4 APDU (DESFire / Payment)

For authentication or reading structured application data from emv or DESFire cards, use NfcTech.IsoDep:

await NfcManager.requestTechnology(NfcTech.IsoDep);
// Send a SELECT AID APDU
const response = await NfcManager.isoDepHandler.transceive([
  0x00, 0xA4, 0x04, 0x00, 0x07,
  0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, // Mastercard AID
  0x00,
]);
// response is number[] — status word is last two bytes
const sw = (response[response.length - 2] << 8) | response[response.length - 1];

This pattern enables EMV card reading, DESFire application selection, and custom APDU command sequences on both Android and iOS.

Background Scanning (iOS)

iOS 13+ supports Core NFC background tag reading without opening a sheet:

// In your App.tsx useEffect
NfcManager.setEventListener(NfcEvents.DiscoverTag, (tag) => {
  // Fires when a tag is scanned while app is foregrounded
  handleTag(tag);
  NfcManager.setAlertMessageIOS('Tag scanned!');
  NfcManager.unregisterTagEvent();
});

await NfcManager.registerTagEvent();

For deeper background processing, configure a Universal Link on the tag's URL so iOS wakes the app. See iOS Core NFC Programming for entitlement details.

React Hook Pattern

Encapsulate NFC logic in a custom hook for clean component usage:

function useNfcScan() {
  const [uri, setUri] = React.useState<string | null>(null);
  const [scanning, setScanning] = React.useState(false);

  const scan = React.useCallback(async () => {
    setScanning(true);
    try {
      await NfcManager.requestTechnology(NfcTech.Ndef);
      const tag = await NfcManager.getTag();
      const record = tag?.ndefMessage?.[0];
      if (record) setUri(Ndef.uri.decodePayload(new Uint8Array(record.payload)));
    } finally {
      setScanning(false);
      NfcManager.cancelTechnologyRequest();
    }
  }, []);

  return { uri, scanning, scan };
}

Common Errors

Error Platform Fix
NfcManagerNotStarted Both Call NfcManager.start() at app root
NFC not supported iOS sim Use a physical device
Tag tech not supported Both Tag type mismatch; check with Compatibility Checker
User cancelled iOS Normal flow; handle gracefully
Transceive failed Android Tag moved; retry with hold prompt

See also: Flutter NFC Development Guide | Android NFC Programming | Web NFC API Guide | NDEF Specification Deep Dive

Terms in This Guide