TL;DR: UUID v4 is the safe default for random unique IDs, ULID is the better choice when you also want IDs that sort by creation time, and NanoID is ideal when you need short, URL-friendly IDs. All three avoid coordination between systems, but they trade off length, sortability, and database index behaviour differently. This guide explains what each one is and when to reach for it.

A unique identifier lets independent systems create records without asking a central server "what's the next number?" That independence is the whole point: two services, or two offline devices, can each mint an ID and stay confident the values will not collide.

UUIDs Explained: When to Use UUID v4, ULID and NanoID
UUIDs Explained: When to Use UUID v4, ULID and NanoID

What a UUID actually is

A UUID is a 128-bit value, normally written as 36 characters — 32 hexadecimal digits plus four dashes, like 550e8400-e29b-41d4-a716-446655440000. The version you almost always want is UUID v4, which fills 122 of those bits with randomness. That yields about 5.3 × 10³⁶ possible values, so a collision is not something you need to engineer around: you would need to generate on the order of a quintillion UUIDs before the probability of any clash reached even 50%.

"Negligible" is not the same as "impossible", but for practical purposes v4 gives you uniqueness without any central coordination.

UUID v4 vs v1

UUID v1 is built from a timestamp plus the generating machine's network (MAC) address. That makes v1 roughly time-ordered, but it can leak where and when an ID was created — occasionally a privacy concern. UUID v4 drops all of that in favour of pure randomness: no ordering, no embedded metadata, nothing to leak. For most applications v4 is the right default, and v1 is only worth it when you specifically want its time component.

ULID: sortable by design

A ULID is also 128 bits, but encoded as 26 Crockford base32 characters instead of hex-with-dashes. Its first 48 bits are a millisecond timestamp and the remaining 80 bits are random. Because the timestamp leads, ULIDs sort lexicographically in the order they were created — sort the strings and you get chronological order for free. That property is genuinely useful: it makes "most recent first" queries cheap and keeps database inserts landing in order rather than scattered.

NanoID: short and URL-friendly

A NanoID defaults to 21 characters drawn from a URL-safe alphabet, so it drops straight into a link without percent-encoding. The length is configurable, which lets you trade size against collision resistance: shorter IDs are tidier but leave less randomness, so keep them longer for high-volume or public identifiers. NanoID shines for share links, short public record IDs, and anywhere a 36-character UUID would look clumsy in a URL.

Which one for a database key?

This is where the choice has real performance consequences. UUID v4 is perfectly valid as a primary key, but its randomness means new rows insert at random positions in a B-tree index, causing fragmentation and more page splits as a table grows. Time-ordered identifiers — ULID, or the newer UUID v7 — insert in roughly increasing order, which keeps the index compact and writes local. If you are choosing a key for a large, write-heavy table, prefer a time-ordered ID; for smaller tables the difference rarely matters.

Common mistakes to avoid

  • Exposing sequential integer IDs publicly when you wanted unguessable ones — that is a job for a random ID, not an auto-increment.
  • Using random UUID v4 as the clustered key of a huge table and then wondering why inserts slow down.
  • Truncating a UUID to "save space" — you throw away the randomness that guarantees uniqueness.
  • Shortening a NanoID too aggressively for a high-volume identifier and raising collision risk.

Match the ID to the job: reach for the UUID Generator to create v4, v1, ULID, or NanoID values, pick v4 for general uniqueness, ULID when order matters, and NanoID when the ID has to live in a URL.