Skip to main content
{/}codefunc
Web & APIJuly 8, 2026 · 6 min read

ULID vs UUID vs Nano ID: Choosing the Right Identifier

A practical comparison of UUID, ULID, and Nano ID — sortability, index performance, collision odds, and URL safety — so you can pick the right ID format for your database, API, and URLs.

By codefunc

uliduuidnanoiddatabaseapiids

Every system needs identifiers, and the default choice — a random UUID v4 — is not always the right one. The wrong ID format shows up later as fragmented database indexes, ugly URLs, or collisions you swore were impossible. UUID, ULID, and Nano ID solve the same problem with very different trade-offs. This guide compares them on the dimensions that actually affect production systems.

Bottom line: Use UUID v4 when you need a universal standard and do not care about ordering. Use ULID (or UUID v7) when IDs are primary keys and you want time-sortable inserts that keep your index healthy. Use Nano ID when you need short, URL-friendly IDs and control over length and alphabet.

Quick comparison

Property UUID v4 ULID Nano ID (default)
Length 36 chars (with dashes) 26 chars 21 chars
Alphabet hex + dashes Crockford Base32 64 URL-safe chars
Time-sortable No Yes (lexicographic) No
Bits of randomness 122 80 ~126
URL-safe Needs no encoding Yes Yes
Standardized RFC 9562 Community spec Community spec

UUID: the universal default

A UUID is a 128-bit value, usually rendered as xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx. Version 4 is almost entirely random:

f47ac10b-58cc-4372-a567-0e02b2c3d479

122 random bits make collisions astronomically unlikely. UUID v4 is the safe, boring, universally supported choice — every language and database understands it.

The catch is randomness kills locality. When a v4 UUID is a primary key, each insert lands at a random position in the index B-tree, causing page splits and cache churn. On write-heavy tables this measurably hurts throughput.

UUID v7 fixes the ordering problem

UUID v7 (part of RFC 9562) prefixes a millisecond timestamp before the random bits, making new IDs monotonically increasing:

018f a1b2 -7c3d -8e4f -a5b6 -c7d8e9f0a1b2
└─ time ──┘

If you want a standardized and sortable UUID, v7 is the modern answer and often a better default than v4 for database keys.

ULID: sortable and compact

A ULID (Universally Unique Lexicographically Sortable Identifier) is 128 bits encoded as 26 Crockford Base32 characters:

01ARZ3NDEKTSV4RRFFQ69G5FAV
└── 48-bit time ─┘└─ 80-bit random ─┘
  • The first 10 characters encode a 48-bit millisecond timestamp.
  • The last 16 characters are randomness.

Because the timestamp is the most significant part, sorting ULIDs as strings sorts them chronologically. That gives you time-ordered inserts (index-friendly, like UUID v7) plus a shorter, case-insensitive, dash-free representation that reads cleanly in logs and URLs.

Crockford Base32 excludes ambiguous characters (I, L, O, U), which reduces transcription errors when a human has to read one aloud.

Nano ID: short and configurable

Nano ID takes a different angle: make the ID as short and URL-friendly as possible while staying collision-resistant. The default is 21 characters from a 64-symbol URL-safe alphabet:

V1StGXR8_Z5jdHi6B-myT

With 21 characters you get roughly 126 bits of entropy — more than UUID v4 — in fewer characters. You can shrink the length or swap the alphabet (for example, digits only, or a no-look-alike set) to trade entropy for brevity.

Nano IDs are not sortable and carry no timestamp. They are ideal for public-facing IDs: short links, invite codes, resource slugs in URLs.

The database angle: why ordering matters

The single most common reason teams switch away from UUID v4 is index fragmentation. A quick mental model:

Key type Insert location Index health
Auto-increment int Always at the end Excellent
UUID v4 Random Poor on write-heavy tables
ULID / UUID v7 Near the end (time-ordered) Good

If your IDs are primary keys on a busy table, prefer a time-sortable format (ULID or UUID v7). If IDs are secondary and you rarely sort by them, v4 is fine.

Collision probability

All three are safe at realistic scale, but the math differs:

  • UUID v4 (122 bits): you would need ~2.7 × 10¹⁸ IDs for a one-in-a-billion collision chance.
  • Nano ID (default, ~126 bits): comparable to UUID v4.
  • ULID (80 random bits per millisecond): collisions are only possible for IDs generated in the same millisecond; even then you need thousands per ms to worry.

Reducing Nano ID length lowers entropy fast — a 10-character Nano ID is fine for low-volume codes but not for high-cardinality keys. Use an entropy calculator before shortening below the default.

Security: IDs are not secrets

A predictable or enumerable ID that gates access is a vulnerability (insecure direct object reference). Two rules:

  1. Do not rely on ULID randomness for security — the timestamp portion is guessable, so a ULID leaks roughly when the record was created.
  2. Do not use any ID as a session token, password-reset token, or API key. Those need dedicated high-entropy secrets and server-side validation.

Use IDs to identify, use authorization to protect.

URL safety

  • Nano ID and ULID are URL-safe by design — no encoding needed.
  • UUID is URL-safe too, but the dashes and length make for long, less readable paths.

If your ID appears in URLs your users see or share, ULID or Nano ID reads better.

How to choose

  • Building a public URL or invite code? Nano ID (tune the length).
  • Choosing a primary key for a write-heavy table? ULID or UUID v7.
  • Need a universal standard with maximum tooling support and do not care about order? UUID v4.
  • Want standardized and sortable? UUID v7.

Generating them

You can generate all of these locally — nothing leaves your machine — with the ULID & Nano ID generator and the UUID generator. For seeding a database with realistic records, pair them with the fake data generator.

// ULID and Nano ID in Node
import { ulid } from "ulid";
import { nanoid } from "nanoid";

const orderId = ulid();      // 01ARZ3NDEKTSV4RRFFQ69G5FAV
const shortRef = nanoid(10); // "V1StGXR8_Z"

Common mistakes

  • Using UUID v4 as a clustered primary key on a high-write table, then wondering why inserts slow down.
  • Shortening Nano ID too aggressively and hitting collisions in production.
  • Treating a leaked ULID as harmless — remember it reveals creation time.
  • Storing UUIDs as text instead of a native/binary column, doubling index size.

Practical takeaway

Match the ID to the job. Time-sortable formats (ULID, UUID v7) keep database indexes healthy; Nano ID keeps URLs short; UUID v4 stays the universal fallback. Never use an identifier as a security token, and check entropy before trimming a Nano ID. Generate and inspect them locally before wiring them into your schema.

Sources

Try it on codefunc

Related articles

3

Search tools

Find a developer tool