Skip to content
nearnull

ULID Generator

Generate sortable, time-ordered ULID identifiers, with optional monotonic mode. Runs entirely in your browser. Nothing is sent to any server.

Options

Identifiers created in the same millisecond keep increasing, so a batch stays in generation order.

ULIDs

Generated ULIDs appear here

What is a ULID?

A ULID is a 128-bit identifier, the same size as a UUID, written as 26 characters instead of 36. Its first 48 bits are the creation time in milliseconds and the remaining 80 are random, so sorting ULIDs as plain strings sorts them by creation time. The encoding is Crockford Base32, which leaves out I, L, O and U: that avoids both the digit/letter confusions when a human reads one out, and the chance of an identifier spelling something unfortunate. Everything is generated in your browser using its cryptographic random source.

How the 26 characters are laid out

1 - 1048 bits

Milliseconds since the Unix epoch, big-endian. This is what makes ULIDs sort chronologically, and it's readable: the tool decodes it back for every value it generates.

11 - 2680 bits

Random bits from the browser's cryptographic generator. With monotonic mode on, these are incremented rather than redrawn within a single millisecond, which keeps a batch in order.

Examples

Generating ULIDs in the languages you're most likely to reach for:

JavaScript
import { ulid } from "ulid";

ulid(); // 01ARZ3NDEKTSV4RRFFQ69G5FAV
Python
import ulid

ulid.new().str
Java
import com.github.f4b6a3.ulid.UlidCreator;

UlidCreator.getMonotonicUlid().toString();
Go
import "github.com/oklog/ulid/v2"

ulid.Make().String()
PHP
use Ulid\Ulid;

Ulid::generate();
PostgreSQL
-- store as a 16-byte value, not text
CREATE TABLE orders (id bytea PRIMARY KEY);

Use Cases

Primary keys that don't fragment the index

Random UUIDs land all over a B-tree index, so every insert dirties a different page. ULIDs are time-ordered, so inserts append near the end of the index, which is why they behave much better as primary keys at volume.

Ordering events without a separate timestamp column

Because the time is embedded in the value, log lines, events and messages sort correctly by id alone, even when they arrive out of order or from different machines.

Identifiers that appear in URLs or get read aloud

26 characters instead of 36, no hyphens, and no I/L/O/U to confuse with 1/0. That makes ULIDs meaningfully easier to paste into a URL, print on a label or dictate over a phone call.

Ids generated on the client

Like UUIDs, ULIDs don't need coordination: a browser, a mobile app or an offline worker can mint one without asking a server, and it stays unique when the data syncs back.

Frequently Asked Questions

ULID or UUID?
Use ULID when identifiers get inserted into a sorted index or need to sort by creation time, and when they're going to be seen by people. Use UUID when a spec, a database column type or an existing system expects that exact format. UUIDv7 is worth knowing about too: it brings the same time-ordering to the standard UUID format.
What does monotonic mode actually change?
Within a single millisecond, ULIDs share the same timestamp prefix, so their order comes down to the random tail, which is effectively arbitrary. Monotonic mode instead increments the previous random value, guaranteeing that each new identifier sorts after the last one. Generating a batch here is exactly that case: all of them share one millisecond.
Do ULIDs leak when a record was created?
Yes, that's the trade. The timestamp is right there in the first ten characters and anyone can decode it, as this tool does. If the creation time of a record is sensitive, or if identifiers being guessable in sequence is a problem, use a fully random identifier such as UUIDv4.
Can two ULIDs collide?
With 80 random bits per millisecond, generating even thousands of identifiers in the same millisecond leaves the collision probability negligible. In monotonic mode collisions within a run are impossible by construction, since each value is the previous one incremented.
Are lowercase ULIDs valid?
The canonical form is uppercase, and that's what you should store and compare. Crockford Base32 decoding is case-insensitive, so lowercase parses fine, but mixing cases in a database column is an easy way to end up with two rows that are the same identifier as far as a human is concerned and different as far as the index is concerned.
How should I store them in a database?
As 16 raw bytes when your database has a fixed-size binary or UUID type, which is half the space of the 26-character text form and keeps comparisons cheap. Store as text only when you need the id to be readable directly in query results.
Are these generated on a server?
No. They're generated in your browser by JavaScript on this page, using the built-in cryptographic random number generator. Nothing is transmitted or stored anywhere.