NDEF Specification Deep Dive
The NFC Data Exchange Format (ndef-message) is the universal data container that makes NFC interoperable across chips, operating systems, and applications. Defined by the NFC Forum, NDEF abstracts away the physical differences between tag types and provides a structured, extensible wire format that any conforming device can read and write.
This guide dissects the NDEF binary layout at the byte level — essential reading before you use the NDEF Encoder or NDEF Decoder in production.
NDEF Message Structure
An ndef-message is a sequence of one or more ndef-record structures packed contiguously in memory. There is no message-level header: the boundary of one message is inferred by the MB (Message Begin) and ME (Message End) flags embedded in each record's header byte.
[ Record 1: MB=1, ME=0 ][ Record 2: MB=0, ME=0 ][ Record N: MB=0, ME=1 ]
A single-record message sets both MB and ME on the same record. The minimal valid NDEF messageNDEF messageComplete data unit containing one or more NDEF recordsView full → is 3 bytes: a 1-byte header, a 1-byte type length, and a 1-byte payload length (SR flag set), with zero-length type and payload.
Record Header Flags
Each record opens with a flags byte that controls how the rest of the record is parsed:
| Bit | Flag | Meaning when set |
|---|---|---|
| 7 | MB | First record in the message |
| 6 | ME | Last record in the message |
| 5 | CF | This record is a chunk (chunked payload continues) |
| 4 | SR | Short record — payload length is 1 byte (0–255 B) |
| 3 | IL | ID Length field is present |
| 2–0 | TNF | Type Name Format (3-bit field, values 0x00–0x07) |
The tnf (Type Name Format) field is the most consequential: it tells the parser how to interpret the TYPE field that follows.
TLV Blocks on Tag Memory
NDEF messages are not written raw to tag memory. On Type 2 and Type 5 tags the memory uses a tlv (Tag-TLV) wrapper:
| TLV Tag | Name | Purpose |
|---|---|---|
| 0x00 | Null | Padding / reserved |
| 0x01 | Lock Control | Identifies dynamic lock bytes |
| 0x02 | Memory Control | Identifies reserved memory areas |
| 0x03 | NDEF Message | Contains the NDEF message (length + data) |
| 0xFE | Terminator | Marks end of TLV stream (single byte) |
For messages ≤ 254 bytes the Length field of the NDEF TLV is a single byte. For messages from 255 to 65535 bytes the length is encoded as 0xFF followed by a 2-byte big-endian value — a detail that trips up many hand-rolled parsers.
The capability-container on Type 2 tags (bytes 0x00–0x03 of the first sector) identifies the tag's memory size and NDEF version, and must be written correctly before the TLV stream or the tag will not be recognised as NDEF-formatted by compliant readers.
NDEF Record Types
The tnf value selects the type namespace:
| TNF Value | Namespace | Common Uses |
|---|---|---|
| 0x00 | Empty | Placeholder records |
| 0x01 | NFC ForumNFC ForumIndustry body developing NFC standards, specifications, and certifications since 2004View full → Well-Known | URI, Text, SmartPoster, Signature |
| 0x02 | MIME type | text/vcard, application/json |
| 0x03 | Absolute URI | Full URI as type |
| 0x04 | NFC Forum External | App-defined types |
| 0x05 | Unknown | Binary blobs without type info |
| 0x06 | Unchanged | Continuation chunks only |
| 0x07 | Reserved | Not used |
Well-Known Types in Practice
- ndef-uri record (type
U, 0x55): Stores a URL. The first payload byte is an abbreviation code (0x01 =http://www., 0x04 =https://, etc.) followed by the remaining URI bytes. A URL ofhttps://nfcfyi.comencodes as[0x04]nfcfyi.com— saving 8 bytes vs storing the full string. - ndef-text record (type
T, 0x54): Stores human-readable text with a language tag. First payload byte encodes UTF-8/UTF-16 flag and language code length, followed by the IETF language code (en,ko) and then the text body. - smart-poster record (type
Sp, 0x5370): A container record whose payload is itself an NDEF message containing at minimum a URI recordURI recordNDEF record encodingencodingData writing to NFC tags during manufacturing productionView full → URIs with compact prefix compressionView full →, optionally augmented with Text (title), Action, Size, Type, and Icon records. - aar record (NFC Forum External type
android.com:pkg): Android Application Record — ensures that when a tag is scanned on Android, the correct app opens or the Play Store launches if not installed.
Chunked Records
The CF (Chunk Flag) bit enables splitting a large payload across multiple physical records. The first chunk sets CF=1, MB=1; intermediate chunks set CF=1, MB=0, ME=0; the final chunk sets CF=0, ME=1. All chunks share the same type and ID — only the first chunk may carry a TYPE field; subsequent chunks use TNF 0x06 (Unchanged).
Chunking is rarely needed in practice because most NFC tags top out at 8 KB, well within a single SR record's reach. However, chunking is required by the spec and must be handled correctly by any conforming parser.
Encoding Workflow
- Determine payload: URL, vCard, JSON, or binary blob.
- Select record type: URI for URLs, MIME for structured data, External for proprietary formats.
- Compute payload bytes and set SR flag if ≤ 255 bytes.
- Wrap in TLV 0x03 with correct length encoding.
- Write capability container first on fresh Type 2 tags.
- Verify with the NDEF Decoder before mass encoding.
Use the NDEF Encoder to generate correct binary output from human-readable inputs, and the NDEF Decoder to inspect raw hex dumps from tag reads. For tag memory sizing, see NFC Memory Calculator.
For the broader tag landscape this format runs on, see NFC Tag Types Explained.