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