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 v4 | Nano ID | ULID | |
|---|---|---|---|
| Length | 36 chars | Configurable (21 default) | 26 chars |
| URL-safe | With hyphens, yes | Yes | Yes |
| Sortable by creation time | No | No | Yes |
| Standard / widely supported | Yes (RFC 4122) | Growing, npm-ecosystem standard | Growing, 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.

