UUID vs ULID vs Nano ID: Which Should You Use?
Every developer reaches for UUID v4 without thinking — it's in every language's standard library and universally supported. But UUID has real problems: it's 36 characters, not sortable, and can fragment B-tree indexes. ULID and Nano ID solve these. Here's when each one is the right tool.
UUID v4 — the safe default
UUID v4 is a 128-bit random number formatted as xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx — 32 hex digits and 4 dashes. The probability of a collision is 1 in 2¹²², which is astronomically small.
Use UUID v4 when:
- You need maximum ecosystem compatibility — PostgreSQL has a native
uuidtype, MySQL supports it, ORMs know how to handle it - IDs are not exposed in URLs (so the 36-char length doesn't matter)
- Your database table is append-heavy and you're using
uuid_generate_v4()orgen_random_uuid()at the DB level
The real problem with UUID v4 is index fragmentation. Because UUIDs are random, each insert lands at a random position in your B-tree index — causing page splits and cache misses at scale. This matters at millions of rows, not thousands.
ULID — when insertion order matters
ULID (Universally Unique Lexicographically Sortable Identifier) looks like this: 01ARZ3NDEKTSV4RRFFQ69G5FAV. It's 26 characters in Crockford Base32 encoding.
The key property: the first 10 characters encode a millisecond timestamp. This means ULIDs generated close together sort together — inserts land near the end of the B-tree index rather than scattered randomly. This is much better for write-heavy workloads.
- Time-sortable — rows with adjacent ULIDs were inserted at nearly the same time
- 26 characters vs 36 for UUID (no dashes)
- Case-insensitive — the alphabet excludes I, L, O, U to avoid visual ambiguity
- 128-bit entropy total (48 bits time + 80 bits random)
Use ULID for database primary keys where you care about index performance, or when you want IDs to carry implicit ordering by creation time (e.g., paginating by ID instead of a separate created_at column).
Nano ID — compact and URL-safe
Nano ID generates short, URL-safe identifiers: V1StGXR8_Z5jdHi6B-myT. The default length is 21 characters using a 64-character alphabet (A-Za-z0-9_-).
- 21 characters by default (configurable down to 6, up to anything)
- URL-safe alphabet — no encoding needed in query strings or paths
- No timestamp component — purely random
- ~126 bits of entropy at the default length (comparable to UUID v4)
Use Nano ID for short IDs that appear in URLs, API keys, session tokens, share links, and anywhere compact size matters more than sortability.
Quick comparison
| Property | UUID v4 | ULID | Nano ID |
|---|---|---|---|
| Length | 36 chars | 26 chars | 21 chars |
| Sortable | No | Yes (by time) | No |
| URL-safe | Yes (with dashes) | Yes | Yes |
| DB index friendly | Poor at scale | Excellent | Poor at scale |
| Ecosystem support | Universal | Good | Good |
The verdict
Default to UUID v4 if you're unsure — compatibility wins. Switch to ULID if you're building a write-heavy service and B-tree index performance matters. Use Nano ID when IDs appear in user-visible URLs and compactness is a priority.
Try it yourself
Use the free browser-based UUID / ULID / Nano ID Generator on DevBench — no signup, runs entirely in your browser.
Open UUID / ULID / Nano ID Generator