UUID v4 vs v7 vs ULID — which ID should you use?
Random, time-ordered, or sortable? A practical comparison of UUID v4, UUID v7, and ULID — with concrete recommendations for databases, APIs, and event streams.
For years the answer to "what ID should I use" was just "UUID v4" and nobody thought about it again. Then people started noticing v4 was quietly making their databases slower, v7 showed up to fix it, and ULID had been sitting there the whole time doing something similar. So now there's an actual decision to make. Here's how to think about it.
The thing they all fix
The reason you reach for any of these instead of an auto-incrementing 1, 2, 3 is independence. A random-looking ID can be generated anywhere — on the client, across ten services, offline — without coordinating with a central counter. No round-trip to the database to ask "what's next," no collisions when two servers generate at the same instant. That's the whole appeal, and all three options give it to you.
Where they differ is what happens after you generate them — specifically, what they do to your database.
UUID v4: random, and that's the problem
v4 is 122 bits of pure randomness. It's the default, it's everywhere, and for a lot of uses it's completely fine.
The catch shows up when you make it a primary key. Database indexes (B-trees) love values that arrive roughly in order — each new row tucks in near the last one. v4 IDs arrive in random order, so every insert lands in a random spot in the index. On a big, busy table that means more page splits, a colder cache, and inserts that get slower as the table grows. You won't notice on a side project. You will notice at scale.
The fix is almost embarrassingly simple — put a timestamp at the front:
Use v4 when the ID is just an identifier and won't be the primary key of a hot table — public-facing IDs, API keys, anything where unpredictability is the point.
UUID v7: same idea, but sortable
v7 keeps the UUID format but puts a millisecond timestamp at the front, followed by randomness. That one change fixes the database problem: because the leading bits increase over time, new IDs sort to the end of the index, the way auto-increment keys do. You keep the generate-anywhere independence of a UUID and lose the insert penalty.
It's a drop-in upgrade — same 128-bit format, same tooling, parses anywhere a UUID parses.
Use v7 when you want a primary key. For most new tables in 2026, this is the right default — it's "UUID v4 but without the downside."
ULID: when you want it readable too
ULID is a different encoding of the same idea — a 48-bit timestamp plus 80 bits of randomness — but written in Crockford Base32 instead of the hyphenated hex format. So 01HQ... instead of f47ac10b-58cc-.... It's shorter, case-insensitive, URL-safe, and sorts lexicographically by creation time.
Functionally it overlaps a lot with v7. The reasons to pick it are ergonomic: it's nicer in a URL, nicer to copy, and sortable as a plain string without parsing. The reason not to pick it is that it isn't a UUID, so anything expecting the UUID format — a Postgres uuid column, libraries that validate the shape — won't take it directly.
Use ULID when the ID shows up in URLs or logs where human-friendliness matters, and you're not tied to the UUID format.
The short version
| You need… | Use |
|---|---|
| A primary key on a table that'll get big | UUID v7 |
| A public ID or API key where unpredictability is the feature | UUID v4 |
| Something sortable and nice in a URL, no UUID-format requirement | ULID |
If you're starting fresh and just want one answer: v7 for keys, v4 for anything you expose. That covers the vast majority of real applications.
Generating and inspecting them
You can generate any of them — in bulk, in whatever format you need — with the UUID Generator. It also has an inspector that tells you which version an ID you paste actually is, which is genuinely handy when you've inherited a database and aren't sure what you're looking at.
Everything's generated locally with the browser's crypto, so the IDs never touch a server — which is exactly what you want, since an ID generator that phones home is an ID generator that's seen all your IDs. Generate a batch at the UUID Generator.

