Skip to content
Utilerio

UUID vs Nano ID vs ULID: Which Unique ID Should You Use?

Three ways to generate a unique identifier, each optimized for a different priority — universality, short URLs, or sortable database keys.

· 3 min read

An auto-incrementing integer ID is the simplest primary key, and also the one that leaks the most: sequential ids reveal exactly how many rows exist and let anyone guess /orders/1002 after seeing /orders/1001. Once you move away from that, there are three common alternatives, and they're not interchangeable — each optimizes for something the others don't.

UUID v4: universal, but long and unsorted

A UUID v4 is 128 bits of randomness, formatted as 36 characters including hyphens (f47ac10b-58cc-4372-a567-0e02b2c3d479). Its entire value proposition is universality: every language, every database, every framework already supports it natively, and the collision probability is low enough to treat as zero for any realistic system.

The trade-off is size and sort order. 36 characters is a lot to put in a URL or a log line, and because the bits are fully random, UUIDs inserted into a database index in no particular order — which fragments B-tree indexes over time and hurts write performance at scale, a real and well-documented problem for UUID-keyed tables.

Nano ID: shorter, URL-safe, same randomness

Nano ID solves the length problem without touching the collision math: it uses a larger alphabet (64 characters instead of hex's 16) to pack the same amount of entropy into fewer characters. The default length of 21 characters has a collision probability comparable to UUID v4 for realistic ID volumes, and because the alphabet excludes characters that need URL-escaping, a Nano ID drops straight into a path segment or query parameter.

This makes it a good fit for public-facing identifiers — short link slugs, invite codes, anything that ends up visible in a URL where 36 characters of UUID would look clumsy.

ULID: sortable by design

Neither UUID nor Nano ID encode when they were created. ULID does: its first 10 characters are a Crockford Base32-encoded millisecond timestamp, followed by 16 characters of randomness — 26 total. Because the timestamp comes first, ULIDs sort lexicographically in creation order, which is exactly the property a UUID lacks and the one that matters most for a database primary key: rows inserted in order stay physically ordered in the index, which is what keeps write performance flat as a table grows.

Side by side

UUID v4Nano IDULID
Length36 charsConfigurable (21 default)26 chars
URL-safeWith hyphens, yesYesYes
Sortable by creation timeNoNoYes
Standard / widely supportedYes (RFC 4122)Growing, npm-ecosystem standardGrowing, explicit spec

Which one, for what

  • Public API resource ids, invite codes, short slugs — Nano ID. Short, URL-safe, and nobody needs to sort by it.
  • Database primary keys, event logs, anything inserted at high volume — ULID. The sortability directly improves index performance, which a random UUID actively works against.
  • Maximum compatibility with existing tooling, or a value that has to match what every other system already expects — UUID v4. It's the one every ORM, database and API client already understands without configuration.

Nano ID / ULID Generator generates both in bulk, and UUID Generator covers the RFC 4122 case with the formatting options (uppercase, braces, hyphens) different systems expect.