UUID vs CUID2
On this page
TL;DR. CUID2 is a 24-character collision-resistant ID format designed for horizontal scaling. It’s shorter than UUID, uses a wider alphabet, and has strong statistical guarantees. For new code, UUID v7 is still the safer default because of universal ecosystem support, but CUID2 is a reasonable choice when you control all clients and want shorter strings.
What CUID2 is
CUID2 is the successor to CUID, designed by Eric Elliott. The format is 24 characters of lowercase alphanumerics:
clo7snjzj0000356ucudl3sn7
The string is generated from:
- A timestamp prefix
- A counter (process-local)
- A hash of (host fingerprint + random bytes)
- A random suffix
CUID2 specifically optimizes for:
- Horizontal scaling — no coordination needed, no host ID assignment.
- Collision resistance — even at billions of IDs across thousands of nodes.
- URL safety — no special characters, fixed width, lowercase only.
- Sortability — IDs are roughly time-ordered (more so than v4, less so than v7).
Side-by-side
| UUID v7 | CUID2 | |
|---|---|---|
| Length (chars) | 36 (with hyphens) | 24 |
| Encoding | hex + hyphens | base36 lowercase alphanumerics |
| Bits of entropy | ~74 random | ~120 (different layout) |
| Time-ordered | yes (lexicographic) | yes (approximately) |
| Standard | RFC 9562 (IETF) | community spec |
| DB native type | yes (Postgres uuid, etc.) | no (stored as text) |
| Prefix safe for URLs | yes | yes |
| Case-sensitive | no (canonical lowercase) | yes (lowercase only) |
| Uses host fingerprint | no | yes (privacy implication) |
Where CUID2 wins
- Shorter strings. 24 chars vs 36 — 33% shorter URLs.
- Lowercase only. No case-confusion bugs (which UUIDs occasionally have when systems normalize differently).
- Wider alphabet. Base36 packs more entropy per character than hex.
- Strong collision-resistance promises. CUID2’s design specifically targets multi-region distributed deployments.
- Designed for horizontal scaling. Built-in host fingerprinting reduces collision risk between machines without manual machine-ID assignment.
Where UUID v7 wins
- Universal ecosystem. Every database has a
uuidtype. Every language has a UUID library. CUID2 needs a third-party package in every language, andtextcolumns in every database. - Standard. UUIDs are an IETF RFC. CUID2 is one community-maintained library. The standard wins for cross-system identity.
- Native database storage. Postgres
uuidis 16 bytes; CUID2 stored astextis ~25 bytes. The 36-vs-24 string-length advantage disappears at the storage layer. - No host fingerprinting. CUID2 derives part of the ID from a hash of host info. For most uses this is fine, but if your threat model considers it a leak (multi-tenant SaaS where customer data shouldn’t reveal infrastructure), UUIDs avoid it.
- Tooling and frameworks. ORMs, routers, OpenAPI codegen, observability tools — they all understand UUIDs natively. CUID2 is a string everywhere it’s not specifically supported.
- Visible time signal. v7 puts a Unix-millisecond timestamp at the start; CUID2’s time component is more abstract. For debugging “when was this row created?”, v7 wins.
Storage comparison
| Approach | Bytes per ID |
|---|---|
Postgres uuid (UUID v7) | 16 |
Postgres text storing CUID2 | ~25 |
Postgres char(24) storing CUID2 | 24 |
Postgres text storing UUID canonical | ~37 |
If you store CUID2 as char(24), the per-row size is 50% more than native UUID. At a billion rows, that’s ~10 GB difference in primary-key index alone.
When to choose CUID2
- You control all clients and systems. Web app, API, mobile, all yours. No partner integrations or external systems consuming your IDs as
uuid. - URL aesthetics matter a lot. Public-facing URLs where 24 vs 36 characters affects user perception.
- You’re deploying across many regions/nodes and the horizontal-scaling story resonates.
- You’re building on top of CUID2-aware tools (some Prisma + serverless tutorials assume CUID2).
When to choose UUID v7
- Anything else. Cross-system identity, broad ecosystem support, native DB type, RFC standard — these compound over years and matter when you didn’t expect to need them.
Code
CUID2
npm install @paralleldrive/cuid2
import { createId, isCuid } from "@paralleldrive/cuid2";
const id = createId(); // "clo7snjzj0000356ucudl3sn7"
const valid = isCuid(id); // true
UUID v7
import { v7 } from "uuid";
const id = v7(); // "01928a47-3b30-7c5e-9d1a-f0b8c4a7e923"
Both are one-line generators. The library API surface is similar.
CUID1 — don’t use
CUID1 (the predecessor, format cjld2cyuq0000t3rmniod1foy) had known weaknesses around collision rate and timing-attack resistance. It’s deprecated. If a tutorial or older codebase says “CUID”, check whether it means CUID1 — and migrate to CUID2 (or UUID v7) if so.
Common pitfalls
- CUID2’s
textstorage hits index size. If you’re going to use CUID2 in Postgres, declare the column aschar(24)(fixed width) rather thantextto avoid TOAST overhead and length prefix. But you still pay 50% more than nativeuuid. - Don’t truncate CUID2. Like UUID truncation, dropping characters reduces collision resistance dramatically.
- CUID2’s host fingerprint isn’t sensitive information by itself, but be aware it exists if you’re auditing what your IDs leak.
Decision flowchart
Are you starting fresh and have no specific reason to choose either?
└── UUID v7
Do you control all clients and want shorter URLs?
└── CUID2 is reasonable, but consider UUID + base64url for similar length
(22 chars) while keeping standard format
Do you cross system boundaries (multiple DBs, message queues, partners)?
└── UUID v7
Are you on a team that already uses CUID2 and likes it?
└── CUID2 (consistency beats marginal switching value)
Try the tools
- UUID v7 generator — modern primary keys
- Short UUID — UUID-shortening alternatives (base64url is 22 chars)
- UUID vs Nanoid — for the “I just need a short ID” case
- UUID as primary key — DB design discussion