How to Generate and Use ULIDs: A Developer's Guide to Sortable Identifiers
Learn how the ULID Generator creates lexicographically sortable, collision-resistant identifiers, and why ULIDs beat UUIDs for modern applications.
Table of Contents
How to Generate and Use ULIDs: A Developer's Guide to Sortable Identifiers
Every modern application generates identifiers β for users, orders, events, files, and database rows. For years the default choice was a UUID v4: a random 36-character string that is virtually guaranteed to be unique. But UUIDs come with a hidden cost. They are unsortable, they bloat database indexes, and they give you no information about when an entity was created.
ULIDs solve this. A ULID (Universally Unique Lexicographically Sortable Identifier) packs a 48-bit timestamp and 80 bits of cryptographic randomness into a compact, 26-character string that sorts naturally in chronological order. You get the collision resistance of a UUID plus a built-in creation time, all in fewer characters and with a friendlier alphabet.
In this guide we'll walk through how ULIDs work, when to choose them over UUIDs or NanoIDs, and how to generate them in bulk with our ULID Generator. The tool runs entirely in your browser β no data leaves your machine β and produces as many identifiers as you need in a single click.
Why Use ULIDs?
- Chronologically sortable by default. Because the timestamp occupies the first 10 characters, a plain string sort of ULIDs reproduces creation order. You no longer need a separate created_at column just to order records.
- Shorter than a UUID. At 26 characters versus a UUID's 36, ULIDs save space in URLs, logs, and database indexes β roughly 28% fewer characters per identifier.
- Case-insensitive and URL-safe. ULIDs use Crockford Base32, which avoids the visually ambiguous characters I, L, O, and U. They survive email clients, QR codes, and voice transcription without confusion.
- Collision-resistant at scale. With 80 bits of randomness, a single millisecond can produce 1.21 Γ 10Β²β΄ unique ULIDs before collisions become a concern β more than enough for any realistic workload.
- Index-friendly for databases. Sequential-ish, time-ordered inserts reduce B-tree fragmentation and write amplification compared to purely random UUIDs, which scatter across the entire key space.
- Spec'd and portable. The ULID specification has language ports in JavaScript, Python, Go, Rust, Java, Ruby, PHP, Elixir, and more. Pick the implementation that fits your stack.
Key Features
| Feature | What it does |
|---|---|
| Crockford Base32 output | 26-character string using 0123456789ABCDEFGHJKMNPQRSTVWXYZ β no I, L, O, or U |
| 48-bit timestamp + 80-bit randomness | First 10 chars encode millisecond time; last 16 chars hold cryptographic entropy |
| Configurable batch count | Generate anywhere from one to thousands of ULIDs in a single action |
| One-click copy + copy-all | Per-item copy buttons plus a Copy-All button for pasting an entire batch |
| Keyboard navigation | Full keyboard support with an aria-live region announcing results for screen readers |
| Client-side only | All generation happens in your browser; nothing is sent to a server |
The generator is powered by the well-established ulid npm package, the same library many production applications depend on. Batch generation is ideal for seeding test databases, pre-allocating ID ranges, or scripting one-off migrations where you need a block of identifiers at once.
Accessibility is built in, not bolted on. Every generated identifier is reachable by keyboard, the results list is announced via an ARIA live region, and contrast meets WCAG guidelines so the tool is usable for everyone on your team.
How to Use the ULID Generator
- Open the ULID Generator.
- In the count input, enter how many ULIDs you want β from a single identifier up to a large batch.
- Click Generate. The results appear instantly in a scrollable list, each with its own copy button.
- Copy individual ULIDs with their per-item buttons, or grab the whole batch with Copy-All.
- Use Clear to reset the list and start a fresh batch whenever you need.
Understanding ULID Anatomy
A ULID is always exactly 26 characters long and splits cleanly into two parts:
01H8XGJF8E ABZQV4N7KDM0P5RT ββ timestamp βββββββ randomness βββββ 48 bits 80 bits 10 chars 16 chars
The timestamp (first 10 characters). These encode a 48-bit Unix millisecond timestamp, giving ULIDs a usable range from 1970 until roughly the year 10889 AD. Because the timestamp leads, sorting ULIDs as strings is identical to sorting by creation time β no parsing required.
The randomness (last 16 characters). These hold 80 bits of cryptographic randomness (typically sourced from crypto.getRandomValues in the browser). That's 2βΈβ° possible values per millisecond, which is why collisions are astronomically unlikely in practice.
Crockford Base32. ULIDs deliberately avoid Base36 in favor of Crockford's Base32 variant. The alphabet 0123456789ABCDEFGHJKMNPQRSTVWXYZ drops I, L, O, and U β the four characters most often misread by humans and OCR systems. The result is case-insensitive, so 01H8XGJF... and 01h8xgjf... represent the same value.
Monotonicity within a millisecond. The spec guarantees that ULIDs generated within the same millisecond sort in the order they were produced. Implementations achieve this by incrementing the random portion when the timestamp hasn't advanced, so even tight loops yield strictly ordered identifiers β a property that matters for event logs and append-only stores.
Practical Use Cases
Database Primary Keys
ULIDs shine as primary keys in PostgreSQL, MySQL, DynamoDB, and other stores. Because they're time-ordered, new rows insert near the end of the index rather than scattering randomly, which keeps B-trees balanced and reduces write amplification. Their string form is also safe to expose in URLs and APIs without leaking row counts the way auto-increment integers do.
Event and Log Ordering
Distributed systems often merge events from many sources into a single stream. Sorting by ULID gives you a near-chronological view without coordinating clocks β each event's identifier carries its own timestamp, so a simple ORDER BY id reconstructs the timeline. This is invaluable for audit trails, change-data-capture pipelines, and append-only event stores.
Distributed Systems
When multiple services or regions generate IDs concurrently, ULIDs avoid the coordination cost of a central sequence. Each node mints identifiers independently using its local clock and randomness source; collisions remain vanishingly improbable thanks to the 80-bit entropy, and the global sort order emerges naturally from the timestamp prefix.
Replacing Auto-Increment
Sequential integer keys reveal business volume (order #1042 tells a competitor you've had about a thousand orders) and complicate sharding. ULIDs are opaque, unpredictable, and shard-friendly β any node can generate a globally unique key without a round-trip to the database.
Best Practices
- Store the canonical uppercase form. ULIDs are case-insensitive, but normalizing to uppercase keeps logs and databases consistent and makes diffs cleaner.
- Don't rely on the timestamp for precise ordering across machines. ULID ordering is monotonic within a process, but clock skew between servers can still cause out-of-order timestamps. Use a logical clock if you need strict cross-host ordering.
- Index the ULID column. Because ULIDs are sortable, a standard B-tree index on the primary key doubles as an index on creation time β you often don't need a separate created_at index.
- Validate input length and charset. When accepting ULIDs from external sources, check for exactly 26 characters from the Crockford alphabet before parsing or storing them.
- Use the right library for your language. Stick to spec-compliant implementations (such as ulid on npm, python-ulid, or oklog/ulid in Go) to guarantee monotonicity and correct encoding.
- Don't encode secrets in ULIDs. The randomness portion is unpredictable but not encrypted β treat ULIDs as identifiers, not as a substitute for access tokens.
Generate Your First ULID
Ready to see sortable identifiers in action? Head to the ULID Generator, set your batch size, and click generate. Whether you're seeding a development database, designing a new event-sourced service, or just curious how 26 characters can encode time and randomness at once, the tool gives you instant, private, copy-paste-ready results.
Happy generating!
Related Tools You Might Like
- UUID Generator β for the classic 36-character UUID v4 when you need maximum ecosystem compatibility.
- NanoID Generator β for short, URL-friendly IDs when sortability isn't a requirement.
- Password Generator β for strong, random passwords with customizable length and character sets.