About the ULID / UUIDv7
This generator creates time-sortable identifiers: ULID and UUID version 7. Both formats put a millisecond timestamp in their leading bits and fill the rest with random data, so sorting the raw strings also sorts the records by creation time. You get the collision resistance of a random ID together with the natural ordering of an auto-incrementing key, without a central counter or coordination between services.
The two formats solve the same problem in different shapes. ULID is a 26-character Crockford Base32 string, case-insensitive and safe to read out loud or paste into a URL. UUID v7 keeps the standard 128-bit, 36-character hyphenated UUID layout defined in RFC 9562, so it drops into any column, ORM or library that already expects a UUID. Neither depends on a database sequence, so multiple services or offline clients can mint IDs independently and still end up roughly in creation order once merged.
Use this over a plain UUID v4 whenever insert order matters: primary keys in an index-organized table, event and log IDs, message keys in a queue, or any record where you want new rows to physically cluster together instead of scattering across the index. It replaces small ad hoc snippets people write by hand (timestamp plus Math.random, or a homegrown Snowflake ID) with an implementation that follows the published specs exactly.
Everything runs locally in your browser using the Web Crypto API for the random bits. No identifier, timestamp or count is sent to a server, so it is safe to generate production-looking keys, test fixtures or seed data for a private project without exposing anything.
How to use it
- Pick the format: ULID or UUID version 7.
- Set how many identifiers to generate, from 1 up to 100 per batch.
- Click generate to create a fresh, time-sortable list instantly.
- Copy a single value, or use copy all to grab the whole list at once.
- Paste the IDs into your migration, seed script or test fixtures.
- Generate again later for more records; newer IDs always sort after older ones.
- For a fully random ID with no embedded timestamp, use the UUID v4 generator instead.
Examples
- ULID for an event record: 01ARZ3NDEKTSV4RRFFQ69G5FAV, sorts by creation time as a plain string.
- UUID v7 for a Postgres primary key: 018f4d2a-7c3e-7b21-9a4d-3e2f1c8b5a90, drops straight into a uuid column.
- Generate 50 ULIDs at once to seed an events table with realistic, ordered test data.
- Use UUID v7 instead of v4 for a new orders table so recent rows stay together on disk.
- Decode the first 10 characters of a ULID back to a Unix millisecond timestamp for debugging.
- Batch-generate UUID v7 values to replace an auto-increment column during a database migration.
How UUID v7 and ULID encode the timestamp
UUID v7 is defined in RFC 9562 (which obsoletes RFC 4122). The first 48 bits hold a big-endian Unix timestamp in milliseconds, giving millisecond resolution until the year 10889. Bits 48 to 51 are fixed to the version marker 0111 (7), and two bits at the start of the next byte are fixed to the variant marker 10, exactly like every other RFC 4122 UUID. The remaining 74 bits are random, split into a small rand_a segment right after the version nibble and a larger rand_b segment after the variant, which the spec allows implementations to use for extra sub-millisecond precision or a monotonic counter. This tool fills them with plain cryptographically secure random bits, which is the simplest RFC-compliant approach.
ULID predates UUID v7 and takes a different route to the same idea. It packs a 48-bit millisecond timestamp and 80 bits of randomness into 128 bits total, then encodes the whole thing as 26 characters of Crockford Base32 instead of hex. Crockford Base32 drops the letters I, L, O and U to avoid transcription mistakes and reads left to right as time first, randomness second, so lexicographic string sort equals chronological sort without decoding anything.
UUID v4 vs UUID v7 vs ULID
The three formats trade off sortability, standards compatibility and encoding in different ways:
- UUID v4: 128 bits, 122 random. Fully random, no order. 36-char hex-with-hyphens. Best when you need maximum unpredictability and do not care about index locality.
- UUID v7: 128 bits, 48-bit timestamp + 74 random. Time-sortable. Same 36-char hex-with-hyphens layout as v4, so it fits existing UUID columns, ORMs and validators unchanged. Best when you want sortability without touching your schema or tooling.
- ULID: 128 bits, 48-bit timestamp + 80 random. Time-sortable. 26-char Crockford Base32, case-insensitive, no hyphens. Best when you want a shorter, URL- and voice-safe string and do not need to fit a strict UUID type.
- Snowflake-style IDs (for comparison): typically 64-bit integers with timestamp, machine/worker ID and sequence counter. Smaller and DB-friendly as a bigint, but need a coordinated worker ID scheme, unlike ULID or UUID v7 which any client can generate independently.
Why time-sortable IDs matter for database indexes
Most relational databases store a table's rows in roughly the physical order of its primary key (directly, for index-organized tables like InnoDB, or indirectly through the clustered primary key index). A random UUID v4 primary key means every insert lands at a random point in that structure: pages split constantly, the working set of hot pages grows because inserts are scattered across the whole index rather than concentrated at the tail, and range scans over recent rows have to touch far more of the index and buffer pool than they should. This is a well-documented problem with UUID v4 as a clustering key in MySQL/InnoDB and Postgres alike.
A time-sortable ID fixes this by keeping the property that made auto-increment integers efficient (new rows append near the end) while keeping the property that made UUIDs useful (no central counter, safe to generate on any node, no collision coordination). New inserts stay close together on disk, recent-first range queries stay cheap, and the ID is still effectively impossible to guess or collide.
Common mistakes and edge cases
A few pitfalls come up repeatedly when adopting either format:
- Storing UUID v7 as a generic CHAR(36) or TEXT column loses the sort benefit in some engines; use the native uuid type in Postgres, or a binary(16) column in MySQL if you need index efficiency at scale.
- ULID sorts correctly as a byte string or as Crockford Base32 text, but not if you accidentally lowercase it inconsistently across systems; comparisons should be case-insensitive or normalized to one case.
- Both formats leak an approximate creation timestamp in the leading bits. Do not use either as a public identifier for records whose creation time should stay private, for example invite codes or password reset tokens.
- Multiple IDs generated within the same millisecond are still unique because of the random tail, but they are not guaranteed to sort in strict generation order within that millisecond, only within their timestamp bucket.
- UUID v7 keeps the classic 8-4-4-4-12 hyphenated layout, so code that already validates "is this a UUID" via regex or a UUID type will accept it without changes; ULID will not pass a standard UUID regex, since it is a different encoding entirely.
Frequently asked questions
Which is better, ULID or UUID v7?
Both are time-sortable and equally safe against collisions. Pick UUID v7 if you need to fit an existing UUID column, ORM or validator without changes. Pick ULID if you want a shorter, case-insensitive, URL-friendly string and do not need strict UUID compatibility.
What is UUID v7 and how is it different from UUID v4?
UUID v7 is defined in RFC 9562 and embeds a 48-bit millisecond timestamp in its first bits, followed by random bits, so it sorts chronologically as a string. UUID v4 is fully random with no timestamp, so it has no natural order.
Is a ULID the same as a UUID?
No. A ULID is a 128-bit identifier like a UUID, but it is encoded as 26 characters of Crockford Base32 instead of the standard 36-character hyphenated hex format, so it will not validate against a UUID type or regex.
Why use a sortable ID instead of a plain UUID v4?
Random UUID v4 primary keys scatter inserts across a database index, which fragments pages and slows range queries on recent data. Time-sortable IDs like ULID and UUID v7 cluster new rows together, improving index locality while staying just as collision-resistant.
Are ULID and UUID v7 still unique enough for production use?
Yes. After the timestamp, ULID has 80 random bits and UUID v7 has 74, both far more than enough randomness that collisions within the same millisecond are astronomically unlikely at any realistic generation rate.
Can I recover the creation time from a ULID or UUID v7?
Yes, the leading bits encode a Unix millisecond timestamp that can be decoded directly. This is useful for debugging but means neither format should be used where the record's creation time must stay hidden.
Does generating IDs here send anything to a server?
No. IDs are generated entirely client-side using your browser's Web Crypto API for randomness. Nothing about the generated values, counts or timestamps leaves your browser.
Can I generate many sortable IDs at once?
Yes, generate up to 100 IDs per batch and copy the full list with one click, which is convenient for seeding a database table or building ordered test fixtures.
Which databases support UUID v7 natively?
Postgres has a native uuid column type that stores any RFC 4122/9562 UUID including v7 efficiently. MySQL and most others store it as CHAR(36) or BINARY(16); some newer database versions are adding native v7 generation functions, but you can always generate v7 values client-side and insert them like any other UUID.
What does the leading part of a ULID or UUID v7 actually encode?
Both encode a 48-bit Unix timestamp in milliseconds since the epoch as their first bits. In a ULID that is the first 10 Crockford Base32 characters; in UUID v7 it is the first 12 hex characters before the version nibble.