Skip to main content

Nano ID Explained: The Short, URL-Safe Alternative to UUID

Why a 21-character Nano ID matches UUID's collision safety in fewer bytes, how the alphabet and length trade off, and when short IDs beat UUIDs.

Published By Li Lei
#nanoid #uuid #unique-id #url-safe #developer-tools

Nano ID Explained: The Short, URL-Safe Alternative to UUID

A UUID is 36 characters, has four hyphens, and was designed in an era when nobody planned to paste an identifier into a URL bar. Nano ID was designed for exactly that. It is a tiny, dependency-free way to produce random identifiers that are shorter, drop cleanly into a path, and carry the same practical uniqueness guarantee. If you have ever looked at /orders/550e8400-e29b-41d4-a716-446655440000 and winced, this is the format you reach for instead.

The headline fact is the one most people get wrong: a Nano ID is shorter than a UUID without being weaker. Those two properties usually trade off against each other, and the reason they don't here is worth understanding before you standardise on it.

What a Nano ID actually is

A Nano ID is a string of random characters drawn from an alphabet you choose. By default that alphabet is 64 URL-safe symbols — A–Z, a–z, 0–9, plus _ and - — and the default length is 21 characters. Every character is independent and fully random; there are no version bits, no variant markers, no layout structure baked into the string. That is the whole specification.

Because there is no structure, a Nano ID drops into places a UUID fights with. It survives a copy-paste into a URL with no encoding, fits a QR code with fewer modules, and slots into a fixed-width database column without the four mandatory hyphens eating space. You can also tune it: shrink the length for a coupon code, swap the alphabet to digits only for an integer-ish parser, or strip out look-alike characters for codes a human will type back.

Why shorter does not mean weaker

The thing that makes an identifier hard to guess and astronomically unlikely to repeat is its entropy, measured in bits — not its character count. This is where the comparison with UUID gets interesting.

A UUID v4 has 128 bits total, but 6 of them are fixed to mark the version and variant. That leaves 122 random bits, rendered as 32 hexadecimal characters plus 4 hyphens. Hex only uses 16 symbols, so each character carries exactly 4 bits.

A default Nano ID uses a 64-symbol alphabet, and 64 is 2 to the 6th power, so each character carries 6 bits. Do the arithmetic: 21 characters × 6 bits = 126 bits of entropy — slightly more than a UUID v4 — in 21 characters versus 36. The Nano ID wins on length precisely because a 64-symbol alphabet packs 50% more information per character than hex does. Same collision safety, fewer bytes, no hyphens.

That is also why "make it prettier by shortening it" is a trap. Entropy scales with length, and cutting characters cuts bits fast.

A worked example: how length moves the odds

Collision math for random IDs follows the birthday bound. With an alphabet of size A and length L, the keyspace is N = A^L, and the number of IDs you can mint before a 1% collision probability is roughly √(2 · N · 0.01).

Take a real Nano ID from the default settings: V1StGXR8_Z5jdHi6B-myT. That is 21 characters from the 64-symbol URL-safe set, so:

  • Keyspace: 64^21 ≈ 4 × 10^37
  • IDs until ~1% collision: about 8 × 10^17

You could mint a million IDs per second for over 20,000 years before a collision became one percent likely. That is the UUID-grade headroom.

Now shorten it. Drop to 6 characters with the same 64-symbol alphabet and the keyspace collapses to 64^6 ≈ 6.9 × 10^10. The 1% collision point falls to roughly 37,000 IDs — a busy weekend of signups, not a millennium. Trim further to a 32-symbol no-confusion alphabet at length 8 and you get about a 40-bit keyspace: fine for a few hundred thousand coupon codes, dangerous as a primary key. The collision readout in the Nano ID generator prints these numbers live, so you size the length against your real write volume instead of guessing.

Choosing the alphabet and length

Two dials, one trade-off. A smaller alphabet needs a longer string to reach the same entropy, and a larger alphabet lets you shorten it. Some common picks:

  • URL-safe, length 21 — the default. UUID-grade uniqueness for opaque database keys or public handles.
  • URL-safe, length 12 — about 72 bits. More than enough for a per-table public ID that hides your auto-increment integers. Your URLs become /orders/V1StGXR8_Z5j instead of /orders/1042, so a competitor can't read your order count off the path or walk to the next one.
  • No-confusion, length 8 — paste ABCDEFGHJKLMNPQRSTUVWXYZ23456789 (no O/0, I/1, l) for coupon or invite codes a tired human retypes from a receipt.
  • Hex or digits — when a downstream parser expects [0-9a-f]+ or something integer-shaped.

One thing the algorithm gets right that hand-rolled versions usually don't: it uses crypto.getRandomValues with bitmask-plus-rejection sampling, not randomByte % alphabet.length. Modulo skews the distribution toward the first few characters whenever the alphabet size isn't a power of two, and it makes the output predictable. If you copy the approach, copy the masking step too — don't just swap the random source.

When to still reach for UUID

Nano ID is not a universal replacement, and pretending otherwise will bite you. Use a UUID when you need a standardised format: a database column typed uuid, an interop contract with a system expecting RFC 9562, or time-ordered keys where UUID v7's embedded timestamp keeps your index append-friendly. Those are real, common reasons, and a Nano ID can't fake a version field it doesn't have. If your situation calls for one, the UUID generator covers v4 and v7.

Reach for Nano ID when the identifier is opaque and you control both ends — public handles, short links, session tokens, fixture IDs, coupon codes. In those cases the shorter, hyphen-free, length-tunable string is just the better tool.

I switched a side project's public order URLs from auto-increment integers to 12-character Nano IDs last year, and the part that surprised me wasn't the security win — it was how much friction disappeared. The IDs fit in a text message, looked deliberate rather than leaked, and I never once had to URL-encode one. I'd reached for a UUID out of habit on the previous project and spent more time fighting its length in fixed-width log columns than I ever spent thinking about Nano ID's collision odds, which the panel had already answered for me.

The short version: pick UUID when the format is the contract, pick Nano ID when the format is yours to decide. For the second case, generate a batch, read the collision number against your real volume, and ship.


Made by Toolora · Updated 2026-06-13