Skip to content
Ink
Esc
↑↓navigate↵open⌘Jpreview
On this page

NFC

Read supported tags through native sessions.

Use @ink/nfc to read tags, send raw commands or emulate a card. Tag writing is not supported.

Read a tag

nfc.read() reads NFC Data Exchange Format (NDEF) records:

import { nfc } from "@ink/nfc";

const tag = await nfc.read({ signal, timeout: 30_000 });
const shortcut = decodeShortcut(tag.records);
await openShortcut(shortcut);

Supply a cancellation signal, a decodeShortcut validator and an openShortcut handler.

Reading waits for a tag while the app is visible. Handle cancellation, timeout, disabled NFC and unsupported tags separately.

Validate tag data

Validate records before using them. Let the user review a tag’s URL before opening it. Tag IDs are not proof of identity.

Session lifetime

Sessions close when leaving the screen or entering the background. Reading NDEF requires neither a raw connection nor card emulation.

Exchange raw commands

Use iso-dep or nfc-a to exchange bytes using the tag’s protocol:

const connection = await nfc.connect({ technology: "iso-dep", signal });
try {
  const response = await connection.transceive(commandBytes, { signal, timeout: 5000 });
  // Decode the response using the tag's application protocol.
} finally {
  await connection.close();
}

Commands and responses are Uint8Array values. The connection reports its technology, serial number and maximum command length.

Limits and cancellation

Only one raw connection or NDEF reader can run at a time. Commands run in order, with a timeout of up to ten seconds and a 64 KiB response limit.

The signal passed to connect() cancels discovery. Close the returned connection when leaving the screen. Cancelling an exchange or backgrounding the app also closes it.

Card emulation

Register static responses

await nfc.emulate({
  aids: ["F000000001"],
  responses: [{ command: "00A4040005F000000001", response: "9000" }],
  fallback: "6D00",
});

This example registers application identifier (AID) F000000001. The listed command returns 9000 (success); other commands return 6D00 (unsupported).

AIDs are hexadecimal values registered in Android’s other category. Static responses work without the app screen open. To remove the registration:

await nfc.stopEmulation();

The device must support host card emulation (HCE) and be unlocked. Payment and secure-element integration are not supported.

Dynamic responses

After registration, handle commands while the app is visible. Supply your protocol’s respondToCommand function and a cancellation signal:

await nfc.handleApdu(async command => {
  return respondToCommand(command);
}, { signal });

Return response bytes including the status word.

deadline defaults to 500 milliseconds and accepts 50–2,000. Late responses are discarded; Ink uses the static response or fallback instead.

Readers may need a faster response, so avoid network requests in the handler.

Backgrounding or aborting the handler restores static responses without removing the AIDs.

Emulation limits

Resource Maximum
Static rules 256
Command or response 4,096 bytes
Registered AIDs 32
JavaScript handlers 1