UUID, ULID, or NanoID? How to Pick the Right ID for Your Next Project
I once joined a project where three different parts of the codebase used three different ID strategies. The user service used UUIDs. The event system used sequential integers starting from 1. The API gateway used random strings of varying lengths that someone had generated with Math.random() and a timestamp concatenated together.
Six months later, the team was dealing with ID collisions in the gateway layer, write performance problems from UUID-keyed indexes in PostgreSQL, and an event ID system that a curious user had figured out was sequential and guessable. Choosing an ID strategy is not glamorous engineering, but getting it wrong creates problems that compound over time.
Starting With UUIDs
UUID stands for Universally Unique Identifier. A version 4 UUID looks like 550e8400-e29b-41d4-a716-446655440000. It is 128 bits of random data formatted as five groups of hexadecimal characters separated by hyphens. The appeal is strong: every major language and database has UUID support, they are practically guaranteed to be globally unique without any coordination between systems, and they reveal nothing about when a record was created or how many records exist.
Generate one with the UUID Generator tool. All generation happens in the browser using the Web Crypto API, which provides cryptographically secure randomness rather than the weak pseudorandomness of Math.random().
The UUID Database Performance Problem
UUIDs have a significant performance drawback in relational databases: they are random, which makes them poor candidates for B-tree index keys. Sequential integer IDs let the database append new entries to the end of an index. Random UUIDs scatter insertions across the entire index, causing page splits and fragmentation that degrades write performance at scale.
In PostgreSQL, a table with millions of UUID primary keys shows measurably worse write performance compared to sequential IDs. This is the real-world problem that alternatives like ULID and UUIDv7 were designed to solve.
ULID: Sortable by Design
ULID stands for Universally Unique Lexicographically Sortable Identifier. A ULID looks like 01ARZ3NDEKTSV4RRFFQ69G5FAV. It is 26 characters, URL-safe, and encodes a millisecond-precision timestamp in the first 10 characters followed by 80 bits of randomness.
The timestamp component solves the database performance problem. ULIDs generated at similar times sort together naturally in an index, resulting in much better write performance. They are also inherently sortable by creation time, which is useful for event logs, audit trails, and any dataset where chronological ordering matters.
The ULID Generator tool generates ULIDs in your browser. Refresh a few times and notice how the first characters change slowly over time while the trailing characters are random each time.
NanoID for Compact, URL-Safe IDs
NanoID generates short, URL-safe random IDs. A typical NanoID looks like V1StGXR8_Z5jdHi6B-myT. At 21 characters it uses a 64-character alphabet of URL-safe characters, packing more randomness into fewer characters than hex-based UUIDs. A 21-character NanoID has collision probability comparable to a UUID v4.
NanoID is a strong choice for short links, public-facing resource IDs in URLs, invitation codes, and anywhere you want an ID that looks clean and short rather than the long, hyphen-separated UUID format. The NanoID Generator tool lets you customize the length and character set.
When You Need a Secret Token
Sometimes what you need is not an ID but a secret: an API key, a password reset token, or a one-time access code. These need to be unguessable and resistant to brute force, which means they need both length and high-quality randomness. The Random String Generator uses the Web Crypto API to generate cryptographically secure random strings with configurable length and character set. This is appropriate for any token that needs to function as a shared secret.
Making the Decision
The right choice depends on a few questions: Do you need sortability and database performance? Choose ULID or UUIDv7. Do you need maximum ecosystem compatibility and tooling support? Choose UUID v4. Do you need compact, URL-friendly IDs for public-facing resources? Choose NanoID. Do you need a secret token rather than a public identifier? Use a secure random string. Understanding these trade-offs upfront prevents the kind of pain that comes from discovering the wrong choice at scale.
