UUID v4 vs v7 vs ULID: Which ID Format Should You Use?
Compare UUID v4, UUID v7, ULID, and NanoID. Learn the differences in randomness, sortability, size, and performance. Choose the right identifier for your database and API.
Try our free UUID / ULID Generator
Generate UUID v4, v7, and ULID. Bulk generate up to 1,000 IDs at once.
Why Your ID Format Matters
Every record in your database needs an identifier. The format you choose affects query performance, index efficiency, storage size, and even security. Auto-incrementing integers are simple but leak information (competitors can estimate your user count) and cause conflicts in distributed systems.
Unique identifiers like UUIDs solve the distributed generation problem — any node can create an ID independently without coordination. But not all UUID versions are equal. The classic UUID v4 (random) has well-documented performance problems with B-tree indexes that UUID v7 and ULID were specifically designed to solve.
In 2024, RFC 9562 formally standardized UUID v7, giving developers an official time-sortable UUID that combines the universality of UUIDs with the database performance of sequential IDs. This guide compares all the major options so you can make an informed choice. Generate any format instantly with our UUID / ULID Generator.
UUID v4: The Random Standard
UUID v4 has been the default choice for over two decades. It generates 122 bits of cryptographically random data, formatted as 32 hexadecimal characters with hyphens: 550e8400-e29b-41d4-a716-446655440000.
Strengths
- Universal support — Every language, database, and framework supports UUID v4. It is built into PostgreSQL, MySQL 8+, Node.js (crypto.randomUUID()), Python, Java, Go, and more.
- No coordination needed — Any machine can generate a UUID v4 independently with virtually zero collision risk.
- No information leakage — Since v4 is purely random, it reveals nothing about creation time, sequence, or the generating machine.
Weaknesses
- Poor index performance — Random values scatter inserts across the entire B-tree index, causing page splits and cache misses. This can degrade write throughput by 2-10x compared to sequential IDs at scale.
- Not sortable — You cannot determine creation order from UUID v4 values. If you need chronological ordering, you need a separate timestamp column and index.
- Large storage size — 36 characters as a string, 16 bytes as binary. This adds up in tables with billions of rows and multiple foreign key references.
// Generate UUID v4 in JavaScript (built-in, no library needed)
const id = crypto.randomUUID();
// → "f47ac10b-58cc-4372-a567-0e02b2c3d479"
// In Node.js
import { randomUUID } from 'crypto';
const id = randomUUID();UUID v7: Time-Sorted and Modern
UUID v7 (RFC 9562, May 2024) solves the index performance problem by embedding a Unix timestamp in milliseconds in the first 48 bits. The remaining bits are random, preserving uniqueness and unpredictability for the sub-millisecond component.
The structure is: [48-bit timestamp][4-bit version][12-bit rand_a][2-bit variant][62-bit rand_b]. Because the timestamp occupies the most significant bits, UUID v7 values naturally sort in chronological order when compared as strings or bytes.
Why UUID v7 Is Better for Databases
In a B-tree index (used by PostgreSQL, MySQL, SQLite, and most databases), new UUID v7 values always insert near the end of the tree because they contain an increasing timestamp. This means:
- No random page splits — writes are sequential, like auto-increment
- Better cache hit rates — recently inserted pages stay in memory
- Range queries by time are free —
WHERE id > [uuid-from-yesterday]works - No separate
created_atindex needed for chronological queries
// Generate UUID v7 in JavaScript (using 'uuidv7' package)
import { uuidv7 } from 'uuidv7';
const id = uuidv7();
// → "018f8c3e-5b2a-7def-8a1c-4f3e2d1a0b9c"
// ^^^^^^^^^^^^^ timestamp portion (sortable)
// Extract timestamp from UUID v7
function getTimestamp(uuid7: string): Date {
const hex = uuid7.replace(/-/g, '').slice(0, 12);
const ms = parseInt(hex, 16);
return new Date(ms);
}ULID: Compact and Sortable
ULID (Universally Unique Lexicographically Sortable Identifier) was created in 2016 as an alternative to UUID with better ergonomics. It encodes 128 bits using Crockford Base32, producing a 26-character string like 01ARZ3NDEKTSV4RRFFQ69G5FAV.
The structure is: [48-bit timestamp (10 chars)][80-bit randomness (16 chars)]. Like UUID v7, the timestamp is in the most significant bits, so ULIDs sort chronologically. The Crockford Base32 encoding is case-insensitive and avoids confusing characters (I, L, O, U).
ULID vs UUID v7 — Which to Pick?
- Size advantage — ULID is 26 characters vs UUID's 36. That is 28% shorter in string form, which matters in URLs, logs, and API responses.
- No hyphens — Easier to select with a double-click, copy from terminal output, and use in file names.
- Monotonic option — ULID libraries can generate monotonically increasing values within the same millisecond, guaranteeing sort order even under high throughput.
- Weaker ecosystem — ULIDs are not an RFC standard. Fewer databases have native ULID types. You may need to store them as strings or convert to binary.
// Generate ULID in JavaScript
import { ulid, decodeTime } from 'ulid';
const id = ulid();
// → "01H5T3K9V2RNGE8AQ1YDMZ6XCW"
// Extract creation timestamp
const timestamp = decodeTime(id);
// → 1690000000000 (Unix ms)Head-to-Head Comparison
| Feature | UUID v4 | UUID v7 | ULID |
|---|---|---|---|
| Size (string) | 36 chars | 36 chars | 26 chars |
| Size (binary) | 16 bytes | 16 bytes | 16 bytes |
| Time-sortable | No | Yes (ms) | Yes (ms) |
| Random bits | 122 | 74 | 80 |
| Standardized | RFC 9562 | RFC 9562 | Community spec |
| DB index perf | Poor | Excellent | Excellent |
| Native DB type | UUID | UUID | String/Binary |
| Embeds timestamp | No | Yes | Yes |
| Case sensitive | No (hex) | No (hex) | No (Base32) |
Generate and compare all three formats with our UUID / ULID Generator. You can also check exact timestamps embedded in IDs using the Timestamp Converter.
Decision Framework: Which to Choose
Use this decision tree:
- Use UUID v7 if: You are building a new application, want database performance + time-sorting, need compatibility with existing UUID columns/types, and want an RFC-standard format. This is the default recommendation for most new projects in 2026.
- Use ULID if: You want shorter IDs (26 chars), need IDs in URLs or user-facing contexts, want monotonic ordering within the same millisecond, or are building in an ecosystem that already uses ULIDs.
- Use UUID v4 if: You need maximum unpredictability (security-sensitive contexts where timestamp leakage is a concern), are working with legacy systems that specifically expect v4, or do not have write-heavy workloads where index performance matters.
- Use NanoID if: You need even shorter IDs (21 chars by default, customizable), are generating client-side IDs in the browser, or need a custom alphabet. Note: NanoID is not time-sortable.
For most applications in 2026, UUID v7 is the best default choice. It gives you the universality and tooling support of UUIDs with the performance characteristics of sequential IDs. If your database is PostgreSQL, you can store UUID v7 in a native uuid column with no changes to your schema.
Frequently Asked Questions
What is the difference between UUID v4 and UUID v7?
Should I use UUID or ULID for my database?
Can UUID v4 have collisions?
Why are random UUIDs bad for database performance?
Generate UUIDs and ULIDs Instantly
Generate UUID v4, v7, and ULID. Bulk generate up to 1,000 IDs. Copy individually or as a list. Free and private.
Open UUID / ULID Generator