UUID vs. NanoID vs. ULID: Choosing the Right ID Generation Strategy
A practical comparison of UUID, NanoID, and ULID — the three most popular unique ID generation strategies, with pros, cons, and guidance on when to use each.
Every application needs unique identifiers. Whether you are generating primary keys for a database, creating session tokens, naming files, or building URLs, the strategy you choose for generating IDs has implications for performance, storage, readability, and collision resistance. The three most popular options today are UUID, NanoID, and ULID. Each was designed to solve different problems, and each makes distinct trade-offs. This guide compares them head-to-head so you can choose the right one for your use case.
UUID (Universally Unique Identifier)
UUIDs are the oldest and most established option. Defined in RFC 4122 (and updated in RFC 9562), UUIDs are 128-bit identifiers represented as 36 characters: 32 hexadecimal digits in five groups separated by hyphens.
Example: 550e8400-e29b-41d4-a716-446655440000
UUID Versions
There are several UUID versions, each with different generation strategies:
- v1 (time-based): Generated from the current timestamp and the machine's MAC address. Guarantees uniqueness across time and space but leaks the MAC address, which is a privacy concern.
- v4 (random): Generated from a cryptographically secure random number generator. The most commonly used version. Has no embedded meaning — just 122 random bits plus 6 fixed bits indicating the version and variant.
- v7 (time-ordered, new): Combines a Unix timestamp in the most significant bits with random data in the remaining bits. Designed to be both unique and sortable — a direct response to the indexing problems of v4.
Pros of UUID
- Universally supported. Every programming language, database, and framework has UUID support built in or available as a standard library. You will never need to write your own implementation.
- Standardized. The RFC ensures interoperability across systems, languages, and organizations.
- Collision-resistant. A v4 UUID has 2^122 possible values. The probability of generating a duplicate is astronomically low — you would need to generate billions of UUIDs per second for centuries before a collision becomes likely.
- Well understood. UUIDs have been in use since the 1990s. Every engineer on your team knows what they are and how they work.
Cons of UUID
- Length. At 36 characters (with hyphens), UUIDs are verbose. This matters in URLs, UI displays, and storage. A v4 UUID takes 16 bytes in binary form but 36 bytes as a string.
- Poor sorting (v4). UUID v4 values are completely random. When used as database primary keys in a B-tree index (InnoDB, PostgreSQL), each insert lands in a random position, causing page splits, fragmentation, and poor write performance. This is the single biggest practical problem with UUID v4.
- No embedded information (v4). A v4 UUID tells you nothing about when it was created. You cannot sort by creation time without a separate timestamp column.
- Not URL-friendly. The hyphens and hexadecimal characters are safe for URLs, but the length makes them unwieldy in user-facing contexts.
NanoID
NanoID is a newer alternative created by Andrey Sitnik. It generates URL-safe unique identifiers using a configurable alphabet and length.
Example: V1StGXR8_Z5jdHi6B-myT (default 21 characters)
How NanoID Works
NanoID uses a cryptographically secure random number generator (like crypto.getRandomValues in browsers or crypto.randomBytes in Node.js) to pick characters from a URL-safe alphabet: A-Za-z0-9_-. The default length is 21 characters, which provides the same collision resistance as a UUID v4 (approximately 2^126 possible values).
Pros of NanoID
- Shorter. At 21 characters (default), NanoID is significantly shorter than a 36-character UUID. You can reduce the length further if your collision tolerance allows it — 10-character NanoIDs are sufficient for many use cases.
- URL-safe. The alphabet uses only characters that are safe in URLs, filenames, and CSS class names without encoding.
- Customizable. You can change the alphabet and length. Need only lowercase letters? Need numeric IDs? NanoID supports custom alphabets.
- Small library. The NanoID package is tiny (under 1 KB minified and gzipped) with zero dependencies.
- Fast. NanoID is optimized for performance and is often faster than UUID generation.
Cons of NanoID
- No standardization. NanoID is a popular open-source library but not an official standard. The format could change, and it is not natively supported in databases.
- No embedded timestamp. Like UUID v4, NanoID is purely random. You cannot extract creation time or sort chronologically without additional columns.
- No built-in versioning or variant. UUIDs have a version field that tells you how they were generated. NanoID has no such metadata.
- Collision tuning required. If you shorten the ID, you need to understand the birthday paradox and calculate collision probabilities for your expected volume. The NanoID documentation provides a helpful collision probability table, but the responsibility falls on you.
When NanoID Shines
NanoID is ideal for:
- Public-facing IDs in URLs (
/post/V1StGXR8_Z5jdHi6B-myT) - Short codes for sharing links or referral codes
- CSS class names or DOM element IDs in generated UI
- Any context where a shorter, URL-friendly ID is preferable to a UUID
ULID (Universally Unique Lexicographically Sortable Identifier)
ULID was designed to address the two main shortcomings of UUID v4: lack of sortability and verbosity. A ULID is a 128-bit identifier encoded as a 26-character string using Crockford's Base32.
Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV
How ULID Works
A ULID consists of two parts:
- Timestamp (48 bits): The first 10 characters encode a Unix timestamp in milliseconds. This gives you sortability by creation time.
- Randomness (80 bits): The remaining 16 characters are cryptographically random.
The encoding uses Crockford's Base32 alphabet (0123456789ABCDEFGHJKMNPQRSTVWXYZ), which excludes I, L, O, and U to avoid confusion with similar-looking characters and accidental profanity.
Pros of ULID
- Sortable. ULIDs sort lexicographically by creation time. This makes them excellent for database primary keys — inserts are sequential, which keeps B-tree indexes efficient and avoids the fragmentation problem of UUID v4.
- Shorter than UUID. At 26 characters, ULID is 10 characters shorter than a UUID with hyphens.
- Embedded timestamp. You can extract the creation time directly from a ULID without storing a separate timestamp. The first 10 characters decode to a millisecond-precision Unix timestamp.
- 128-bit compatibility. Under the hood, a ULID is 128 bits — the same as a UUID. You can store it in a
UUIDcolumn in PostgreSQL or any system that accepts 128-bit values. - Monotonic sorting. The ULID specification includes an optional monotonicity feature where successive IDs generated in the same millisecond are guaranteed to be strictly increasing, even without a shared counter.
Cons of ULID
- Less ecosystem support. ULID is newer and less widely supported than UUID. You may need to add a library in your language or ORM, and some databases do not have native ULID types.
- Crockford's Base32 is unfamiliar. The encoding is not standard Base32 and requires a specific library for encoding and decoding.
- Still longer than NanoID. At 26 characters, ULID is longer than a default NanoID (21 characters), though shorter than a UUID (36 characters).
- No IETF standard. Unlike UUID (RFC 4122/9562), ULID is defined by a GitHub specification, not a formal standards body.
Head-to-Head Comparison
| Property | UUID v4 | NanoID | ULID | |---|---|---|---| | Length (string) | 36 chars | 21 chars (default) | 26 chars | | Length (binary) | 16 bytes | N/A (string only) | 16 bytes | | Sortable | No | No | Yes (by time) | | Timestamp embedded | No | No | Yes (millisecond) | | URL-safe | Yes (with hyphens) | Yes | Yes | | Standardized | Yes (RFC) | No | No (GitHub spec) | | Database support | Universal | Limited | Growing | | Collision resistance | 2^122 | 2^126 (default) | 2^80 (per ms) | | Alphabet | Hex (0-9, a-f) | Customizable | Crockford Base32 |
Making the Choice
Choose UUID v4 when:
- You need maximum compatibility across systems and languages.
- Your database has native UUID support and you are using an ORM that handles the mapping.
- Sort order does not matter (you have a separate
created_atcolumn for ordering). - You are working in a regulated environment where standardized identifiers are preferred.
Choose UUID v7 when:
- You want UUID compatibility but need time-ordered IDs.
- You are starting a new project and can adopt the newer RFC 9562 standard.
- You use PostgreSQL 17+ which has proposed UUID v7 support.
Choose NanoID when:
- You need short, URL-friendly identifiers for public-facing resources.
- You are generating client-side IDs (browser APIs, React keys).
- You want a customizable alphabet or length.
- Sort order is handled by a separate column.
Choose ULID when:
- You need sortable unique identifiers as primary keys in a database.
- You want to extract creation timestamps from the ID itself.
- You are building event-sourced systems where chronological ordering matters.
- You want 128-bit compatibility with existing UUID infrastructure.
Practical Tips
-
Do not mix ID strategies in the same column. If your
users.idcolumn uses UUIDs, do not start inserting NanoIDs into it. Pick one strategy per entity and stick with it. -
Consider the URL test. If an ID will appear in a URL that users see or share, shorter is better. NanoID or ULID wins over UUID v4 in these cases.
-
Benchmark your database. Before committing to a strategy, benchmark insert performance with your expected data volume. The difference between sequential (ULID) and random (UUID v4) inserts becomes significant at millions of rows.
-
Use binary storage when possible. Storing UUIDs or ULIDs as 16-byte binary values instead of 36-character strings saves 56% storage and improves index performance in most databases.
-
Generate IDs server-side for security. While client-side generation is convenient, server-side generation lets you audit and control ID creation, which matters for security-sensitive resources.
Conclusion
There is no universally "best" ID generation strategy. UUID v4 remains the safest default due to its universal support and standardization. NanoID is the best choice when you need short, URL-friendly identifiers. ULID is the best choice when you need sortable IDs with embedded timestamps. Many projects use different strategies for different entities — NanoID for public URLs, UUID for internal references, and ULID for event logs. Understanding the trade-offs lets you make an informed choice for each use case rather than defaulting to one approach everywhere.