
UUID vs ULID vs Nanoid: Which Unique ID Should You Use?
Random UUID v4, sortable UUID v7 and ULID, or compact customizable Nanoid: a comparison and online generators for primary keys, public IDs and tokens.
Random, sortable, or compact: three different jobs for one string
A plain auto-increment leaks how many rows a table has, coordinates poorly across multiple servers writing at once, and is guessable — ID 1042 implies an ID 1041 exists. That is why apps generate IDs client-side or server-side without a central counter. But "unique and unpredictable" does not mean the same thing across formats. The UUID generator produces a purely random UUID v4, a UUID v7 whose leading bits encode a timestamp (so it sorts), or the nil UUID for testing. The ULID generator targets the same goal as UUID v7 — an ID that sorts by creation time — with a shorter Base32 string. The Nanoid generator drops the embedded timestamp entirely to optimize string length for a given entropy budget. All three run fully in the browser and sit together on the developer utilities hub, next to the regex tester and the password generator.
Picking the right tool for the context
Prototyping, testing, and visual checks before writing any server-side code.
The online generator prototypes the format; it does not replace production-scale generation.
Mistakes that get expensive once you are in production
A primary key filled with UUID v4 values inserts every new row at a random position in the B-tree index, fragmenting pages and slowing writes on a high-volume table. That is not a UUID problem in general — it is a pure-randomness problem: UUID v7 (RFC 9562) encodes a millisecond timestamp in its leading 48 bits and fills the rest with randomness, and ULID does the same idea in Base32. Both append to the end of the index, like an auto-increment, without exposing a usable counter. The UUID generator switches from v4 to v7 without changing the field shape (36 hyphenated characters); the ULID generator offers the shorter 26-character alternative.
Replacing an auto-increment with a UUID, ULID, or Nanoid stops trivial enumeration (trying ?id=124, then 125…) but checks nobody's authorization: an ID obtained through another channel — a shared link, a log line, a referrer header — is still readable if the API does not verify ownership of the resource. ULID adds a specific problem: its first 10 characters encode the creation timestamp in plain sight. That is harmless for a primary key, but not for a token meant to stay opaque — anyone who roughly knows the creation time narrows the search space before even attacking the random portion.
Nanoid's collision resistance depends entirely on size × alphabet. The tool's default (21 characters over the 64-symbol url alphabet) targets a collision probability comparable to a UUID v4 at a realistic generation rate — but every preset changes that math. Switching to the "numeric" alphabet (10 symbols) or dropping to 6-8 characters for a shorter slug can collapse the ID space by several orders of magnitude, with nothing flagging it except the live entropy counter next to the size field.
The UUID tool's format switch (plain, braces, urn:uuid:) and case toggle change the text representation, not the underlying 128 bits — but a column doing plain string comparison does not know that {550e8400-…} and urn:uuid:550e8400-… name the same UUID without normalization first. Same logic for ULID: Crockford Base32 is defined as case-insensitive on decode, but a string UNIQUE constraint is case-sensitive.
Format, size, sort order: what actually separates the four
Same goal — uniqueness without a central coordinator — four different shapes.
The real options on each tool, as they exist today.
Three concrete situations where the format choice has a measurable effect.
Generate, adjust, copy
On the UUID generator, the list updates as soon as you change an option — there is no separate "generate" button. On the ULID generator, the decoded timestamp of each ID appears right under the generated row.
Step 1 — Pick the version or the sort option
UUID: v4 for pure randomness, v7 for sortability, nil for the all-zero test UUID (the count is then forced to 1). ULID: turn on monotonic mode if you generate several IDs within the same millisecond and need a guaranteed order.
Step 2 — Adjust hyphens, case, and format
On UUID, picking the urn:uuid: format automatically forces hyphens on and disables the case toggle. On ULID, uppercase is on by default; Crockford Base32 decodes correctly even in lowercase.
Step 3 — Set the count (1 to 50), then copy
Copy the whole list at once, download it as .txt, or copy one row at a time with the per-row button.
The Nanoid generator shows live entropy in bits the moment you change the size or alphabet, so you never have to calculate it by hand.
The same formats server-side, in Node
Node exposes crypto.randomUUID() natively for a UUID v4, no dependency needed. Node's built-in crypto module does not generate v7 — a small npm package like uuid handles the sortable variant.
Basic example
Node has no built-in ULID support. The ulid package exposes generation plus decoding of the embedded timestamp.
Basic example
The nanoid package matches the tool's default url alphabet and default size (21 characters), with customAlphabet for a custom alphabet or size.
Basic example
No install, nothing uploaded, up to 50 IDs per batch, full format options for all three formats. Trade-off: no high-throughput generation in the browser, no monotonicity guarantee across tabs or processes, and a custom Nanoid alphabet capped at 256 characters. Use the pages to prototype a schema or produce fixtures; keep uuid, ulid, and nanoid server-side for production.
Conclusion
There is no single "best" identifier. UUID v4 remains the right call for a purely opaque ID with no sort requirement and the widest native support. UUID v7 or ULID take over once insertion order matters for index performance or for an application-level sort without a dedicated created_at column. Nanoid targets the shortest possible string at a given entropy budget, especially in a URL. None of the three replaces a server-side authorization check, and none is meant for high-throughput generation in the browser — that is what the uuid, ulid, and nanoid server libraries are for. If these IDs travel inside an API's JSON payload, the guide on optimizing a JSON REST API pairs well with this one, and so does the JSON Schema validator for checking the expected shape of an id field.
Related Articles

Compute an MD5 or SHA digest, or sign a message with HMAC, in the browser. Hex, Base64 or Base64URL — text checksums and webhook signatures, 100% local.

Skip SSH and nginx for a side-project Next.js app. Compare Railway, Render, Vercel and a VPS, then ship from Git after minifying assets in the browser.

Encode or decode text, a file, or an image as Base64 in the browser — data URIs, email attachments, JWT segments. 100% local, not encryption.