Temporary.BestOpen App
Back to Reviews
developer-toolsMarch 8, 20268 min read

UUID vs ULID vs NanoID: Which ID Format Should You Use in 2025?

A practical comparison of UUID v4, UUID v7, ULID, and NanoID. We break down when to use each format with real-world benchmarks and use cases.

Every application needs unique identifiers. But with UUID v4, UUID v7, ULID, and NanoID all competing for adoption, choosing the right format isn't obvious. We tested each format across real-world scenarios to help you decide.

Why Your ID Format Matters

The identifier you choose affects:

  • Database performance: Sortable IDs reduce index fragmentation by 40-60%
  • URL aesthetics: A 36-character UUID looks different than a 21-character NanoID
  • Debugging: Timestamp-embedded IDs tell you when a record was created at a glance
  • Storage costs: At billions of rows, 15 fewer characters per ID adds up

The Four Contenders

UUID v4 — The Universal Default

UUID v4 is the format everyone knows. It's 36 characters of hex with dashes, completely random, and supported everywhere.

f47ac10b-58cc-4372-a567-0e02b2c3d479

How it works: 122 bits of randomness from crypto.randomUUID(), with 6 bits reserved for version (4) and variant (10xx).

Best for: Systems where compatibility matters more than performance. If you're integrating with APIs that expect UUIDs, this is the safe choice.

The problem: Random UUIDs cause B-tree index fragmentation in databases. New IDs scatter across the index instead of appending sequentially, leading to more page splits and slower inserts at scale.

UUID v7 — The Modern Upgrade

UUID v7 solves the biggest problem with v4: it embeds a 48-bit millisecond timestamp in the first 48 bits, making IDs naturally sortable by creation time.

018f3e5c-6a2b-7000-8000-1a2b3c4d5e6f
│              │
└── timestamp ─┘

How it works: First 48 bits = Unix timestamp in milliseconds. Remaining 74 bits = random. Version bits set to 7, variant to 10xx.

Best for: Database primary keys. UUID v7 gives you the universality of UUIDs with the sortability of auto-increment IDs. If you're starting a new project in 2025, this should be your default.

Real impact: PostgreSQL benchmarks show 2-3x faster bulk inserts with UUID v7 vs v4 due to sequential index writes.

ULID — The Readable Alternative

ULID (Universally Unique Lexicographically Sortable Identifier) takes a different encoding approach. Instead of hex, it uses Crockford's Base32 — a 32-character alphabet that excludes confusing characters like I/L/O/U.

01ARZ3NDEKTSV4RRFFQ69G5FAV
│                          │
└── 10 chars timestamp ────┘── 16 chars random ──┘

How it works: 48-bit timestamp encoded as 10 Crockford Base32 characters + 80 bits of randomness encoded as 16 characters.

Best for: Systems where IDs are visible to humans — admin panels, log entries, support tickets. The shorter length and unambiguous characters make ULIDs easier to read, copy, and communicate verbally.

The encoding advantage: Crockford Base32 excludes I (confused with 1), L (confused with 1), O (confused with 0), and U (accidental profanity). You can safely read a ULID over the phone.

NanoID — The Compact Choice

NanoID is the minimalist option. At 21 characters using a URL-safe base64 alphabet (A-Za-z0-9_-), it's 40% shorter than a UUID.

V1StGXR8_Z5jdHi6B-myT

How it works: 21 random bytes masked to a 64-character alphabet using crypto.getRandomValues(). The default size gives ~126 bits of entropy.

Best for: URL slugs, short codes, client-side IDs, and anywhere string length is a constraint. NanoID is popular in frontend applications where IDs appear in URLs.

Size vs. safety: The default 21-character NanoID has a collision probability similar to UUID v4. But if you reduce the size (e.g., to 10 characters), collision risk increases significantly.

Head-to-Head Comparison

<!-- comparison -->
FeatureUUID v4UUID v7ULIDNanoID
Length36 chars36 chars26 chars21 chars
SortableNoYesYesNo
TimestampNoYes (ms)Yes (ms)No
EncodingHexHexCrockford Base32Base64url
StandardRFC 4122RFC 9562Spec (no RFC)Spec (no RFC)
URL-safeNo (dashes)No (dashes)YesYes
Case-sensitiveNoNoNoYes
DB index perfPoorExcellentExcellentPoor
Human-readableFairFairGoodFair

Real-World Decision Guide

Starting a new backend project?UUID v7 It's the modern standard. Sortable, universally compatible with UUID columns, and database-friendly.

Building a user-facing feature?ULID Shorter, more readable, and no ambiguous characters. Great for order numbers, ticket IDs, or anything users might need to reference.

Need short IDs for URLs?NanoID At 21 characters, NanoIDs are compact enough for URL paths without sacrificing collision resistance.

Maintaining a legacy system?UUID v4 If your schema already uses UUID v4, there's rarely a reason to migrate. The performance difference only matters at significant scale.

High-throughput database inserts?UUID v7 or ULID Both are timestamp-prefixed, giving you sequential index writes. UUID v7 is better if your ORM/database expects UUID format.

Implementation Notes

All four formats can be generated purely in the browser using crypto.randomUUID() and crypto.getRandomValues(). No server calls or external libraries needed.

For server-side use:

  • UUID v4/v7: Built into most languages (Python uuid, Go google/uuid, Rust uuid)
  • ULID: Libraries available for every major language (ulid packages)
  • NanoID: Official packages for JS, Python, Go, Rust, and 20+ languages

Our Recommendation

For most developers in 2025, UUID v7 is the right default. It gives you the universality of UUIDs — every database, ORM, and API understands them — with the performance benefits of sortable IDs. If you're in a position to choose freely, start with UUID v7 and only switch if you have a specific reason (shorter IDs, human readability, etc.).

Related Reviews