Skip to content
LocalOnly
Developer Utilities

Nano ID vs UUID vs Auto-Increment IDs Compared

Every new table and every public URL needs an ID scheme. Here is how Nano ID, UUID and auto-increment compare, with the collision maths worked out and a tested Nano ID function.

Updated October 18, 20263 min readTool runs in your browser

Nano ID vs UUID vs a plain auto-increment number is a choice every new table and every public URL forces on you. The Nano ID Generator creates IDs in the browser, and its page explains how alphabet and length trade off. This guide compares the three schemes as a design decision.

Nano ID vs UUID vs auto-increment at a glance

                 Example                                Length  Random bits
Auto-increment   48213                                  grows   none
UUID v4          7e338c66-c54b-426e-b18a-030cb358d321  36      122
UUID v7          01a14e9b-ce01-7b63-b660-535fa2569f01  36      74, plus a timestamp
Nano ID          4YI8VTtMxq1wiWgdbvHkm                  21      126

A default Nano ID carries slightly more randomness than a UUID v4 in 21 characters instead of 36, because each character holds 6 bits from a 64-character alphabet of letters, digits, _ and -, all safe in URLs.

How likely is a collision?

For random IDs, the chance of any two being equal grows with the square of how many you create. Worked out for a one-in-a-million chance of a single collision:

ID              Random bits   IDs before a 1-in-a-million collision chance
UUID v4         122           about 3.3 quadrillion (3.3 x 10^15)
Nano ID, 21     126           about 13 quadrillion (1.3 x 10^16)
Nano ID, 10      60           about 1.5 million
Nano ID, 8       48           about 24,000

Default lengths are safe for any realistic system. Short IDs are where the risk is: an 8-character ID reaches a one-in-a-million collision chance after about 24,000 IDs, which a busy table reaches in a day. If you shorten IDs, add a unique constraint and retry on conflict.

What each ID reveals in a URL

  • Auto-increment tells anyone how many records exist and how fast they grow: /orders/48213 today and /orders/48913 next week says you took about 700 orders. It also invites guessing neighbours, so every endpoint must check that the caller may see that record.
  • UUID v7 reveals when the record was created, to the millisecond, because the timestamp is part of the ID.
  • UUID v4 and Nano ID reveal nothing. They are still not access control: check permissions on every request, whatever the ID looks like.

How they behave in a database

Auto-increment and UUID v7 values grow over time, so new rows land at the end of the primary key index. Fully random IDs such as UUID v4 and Nano ID land anywhere in the index, which spreads writes across many pages and makes the index larger on very large tables. A common compromise is an internal auto-increment or UUID v7 primary key, plus a random public ID in a separate unique column for URLs.

Generate a Nano ID without a library

const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-";

function nanoid(size = 21) {
  const bytes = crypto.getRandomValues(new Uint8Array(size));
  let id = "";
  for (const byte of bytes) id += alphabet[byte & 63];
  return id;
}

byte & 63 keeps the lowest 6 bits of each random byte, which picks every character with equal probability because the alphabet has exactly 64 characters. That only works for an alphabet of exactly 64. Check the length if you change it: with 63 characters, one value in 64 would point past the end of the string. For other alphabet sizes, use the nanoid package, which handles the general case.

Frequently asked questions

Command Palette

Search for a tool or command