Skip to main content
Toolsbase Logo

UUID v4 / v7 / NanoID / CUID2 — A Complete Comparison Guide

Toolsbase Editorial Team
UUIDNanoIDCUID2RFC 9562IdentifierDatabase

Why Identifier Choice Matters

Identifiers are a foundational decision in distributed systems and database design. A seemingly simple choice — "just use UUID" — can later lead to index fragmentation, poor insertion performance, or unintended data leakage through timestamp exposure.

UUID v4, UUID v7, NanoID, CUID2 — each has a distinct design philosophy. This guide compares them based on their specifications and helps you choose the right one for your use case.


UUID Specification Overview (RFC 9562)

The current UUID specification is RFC 9562, published in May 2024. It obsoletes RFC 4122 and formally defines UUID versions 6, 7, and 8 for the first time.

A UUID is a 128-bit value, commonly represented as a hyphenated hexadecimal string: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx. The 4-bit version field (M) and variant bits (N) are embedded in the structure.

Version Generation Method Time-Ordered
v1 Timestamp + MAC address Yes (imperfectly)
v4 Fully random No
v7 Unix Epoch + random Yes

UUID v4 — The Standard Random Identifier

Structure

Of UUID v4's 128 bits, 122 bits are random. The remaining 6 bits encode the version (0100) and variant (10).

xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
                ^         ^
            version=4   variant bits

Collision Probability

The 122-bit random space contains 2^122 (approximately 5.3 × 10^36) possible values. By the birthday paradox, a 50% collision probability is reached only after generating approximately 2.71 × 10^18 UUIDs. In practice, collisions are negligible without deliberate attack.

// Node.js built-in crypto module
const { randomUUID } = require('crypto');
const id = randomUUID();
// => "f47ac10b-58cc-4372-a567-0e02b2c3d479"

// Browser Web Crypto API
const id = crypto.randomUUID();

Key Characteristics

  • Unpredictability: Uses a cryptographically secure pseudorandom number generator (CSPRNG), making consecutive IDs impossible to predict
  • No time-ordering: Random insertion positions degrade B-tree index performance at scale
  • Universal support: Native support in virtually all languages and frameworks

Best For

  • Resources exposed in URLs where creation time should not be inferred
  • Small to medium datasets where insertion performance is acceptable
  • Projects that prioritize simplicity

UUID v7 — Time-Ordered Random Identifiers

UUID v7, formally defined in RFC 9562, combines a Unix millisecond timestamp with random bits, making it both time-ordered and suitably unpredictable.

Structure

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           unix_ts_ms                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          unix_ts_ms           |  ver  |       rand_a          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var|                        rand_b                             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                            rand_b                             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The upper 48 bits carry the Unix millisecond timestamp (unix_ts_ms). The remaining bits consist of the version, variant, and 74 bits of random data (rand_a / rand_b).

// uuidv7 package (npm)
import { uuidv7 } from 'uuidv7';

const id1 = uuidv7(); // "0191c0f2-87b2-7000-b3c5-8a1e3b2d5678"
const id2 = uuidv7(); // "0191c0f2-87b4-7001-a2f3-7c9d0e1f2a3b"
// String comparison: id1 < id2 (chronological order preserved)

Why UUID v7 Is Database-Friendly

Most databases (MySQL InnoDB, SQL Server) use B-tree clustered indexes for primary keys. UUID v4's random values cause frequent mid-page insertions, leading to page splits and fragmentation.

UUID v7's timestamp prefix means new records are almost always appended at the end, dramatically reducing fragmentation. PostgreSQL benchmarks with large datasets report 30–50% faster insertions compared to UUID v4.

// TypeScript: Drizzle ORM with UUID v7
import { uuidv7 } from 'uuidv7';
import { pgTable, uuid, text } from 'drizzle-orm/pg-core';

const events = pgTable('events', {
  id: uuid('id').primaryKey().$defaultFn(() => uuidv7()),
  name: text('name').notNull(),
});

Best For

  • Primary keys on high-write tables
  • Records that need time-based sorting or cursor pagination
  • Retaining UUID's standard 128-bit format while improving database performance

NanoID — Compact, URL-Safe Identifiers

NanoID by Andrey Sitnik was released in 2017 as a lightweight alternative to UUID. By default it generates a 21-character string using a URL-safe alphabet.

Design Philosophy

NanoID uses a configurable alphabet (default: A-Za-z0-9_-, 64 characters) and configurable length, producing more entropy per character than UUID.

Default (21 chars, 64-char alphabet):
Random bits = 21 × log2(64) = 21 × 6 = 126 bits

This slightly exceeds UUID v4's 122 bits while using only 21 characters.

import { nanoid, customAlphabet } from 'nanoid';

const id = nanoid();       // "V1StGXR8_Z5jdHi6B-myT"
const shortId = nanoid(10); // "IRFa-VaY2b"

// Numbers only
const numericId = customAlphabet('0123456789', 12);
console.log(numericId()); // "483920571038"

// Human-readable (no ambiguous chars like 0/O, 1/l)
const humanId = customAlphabet('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', 8);
console.log(humanId()); // "A7XK3M2P"

Comparison with UUID v4

Property UUID v4 NanoID (default)
Length 36 (with hyphens) 21
Random bits 122 126
URL-safe Partially (hyphens are safe) Yes
Time-ordered No No
Bytes (UTF-8) 36 21
Standards body RFC 9562 None

Best For

  • IDs used directly in URLs (short link slugs, public resource identifiers)
  • Frontend or mobile apps where bundle size matters
  • Scenarios where UUID's length is unsuitable for the UI

CUID2 — Collision Resistance for Distributed Systems

CUID2 is a 2022 redesign of the original CUID by Eric Elliott. While the original relied on timestamps and counters, CUID2 uses SHA-3 hashing to eliminate timestamp-based patterns.

Design Goals

  1. Collision resistance in distributed environments: Multiple servers and processes generating IDs simultaneously should not collide
  2. No embedded timestamps: The creation time cannot be inferred from the ID
  3. Fingerprint entropy: Environmental information (e.g., a hash of the hostname) is mixed into the entropy pool
import { createId } from '@paralleldrive/cuid2';

const id = createId(); // "clh3x5y9b0000qzrmn3b24bmi"
// Always lowercase alphanumeric; first character is always a letter

Internal Structure

[first char (letter)] + [SHA-3 hash of (timestamp + fingerprint + random)]

Default length is 24 characters. You can customize both length and fingerprint:

import { init } from '@paralleldrive/cuid2';

const createCustomId = init({
  length: 32,
  fingerprint: 'my-app-server-1',
});

console.log(createCustomId()); // 32-character string starting with a letter

Best For

  • Microservices where many servers generate IDs simultaneously
  • Applications where ID creation time must not be exposed
  • Systems that require lowercase alphanumeric IDs

Full Comparison Matrix

Property UUID v4 UUID v7 NanoID CUID2
Random bits 122 74 126 ~160+
Time-ordered
URL-safe Partially Partially
Specification RFC 9562 RFC 9562 None None
Default length 36 36 21 24
DB insert efficiency (large scale) Low High Low Low
Timestamp predictability None 48-bit timestamp None None
External dependencies None Required Required Required

Choosing a Primary Key for Your Database

Database-Specific Guidance

PostgreSQL: Stores UUID as a native 128-bit type with minimal overhead. UUID v4 performs acceptably at moderate scale, but UUID v7 is worth considering once insertion rates exceed a few thousand per second on indexed tables.

MySQL / MariaDB: InnoDB's clustered index makes UUID v4's random insertions especially costly. UUID v7 or storing UUIDs as BINARY(16) significantly reduces fragmentation.

-- MySQL: store UUID as binary for better performance
CREATE TABLE users (
  id BINARY(16) NOT NULL DEFAULT (UUID_TO_BIN(UUID(), 0)),
  email VARCHAR(255) NOT NULL,
  PRIMARY KEY (id)
);

-- Retrieve as text
SELECT BIN_TO_UUID(id) AS id, email FROM users;

MongoDB: MongoDB's default ObjectID is a 12-byte value with a timestamp prefix — structurally similar to UUID v7. When switching to UUID in MongoDB, UUID v7 is a natural fit.

Security Consideration

UUID v7's upper 48 bits are a Unix millisecond timestamp. When exposed in URLs, the creation time of the resource can be estimated. If this is unacceptable, use UUID v4 or NanoID instead.

https://example.com/api/items/0191c0f2-87b2-7000-b3c5-8a1e3b2d5678
                               ^^^^^^^^^^^^^^^^
                         Timestamp: approximately mid-2024, inferrable

Practical Implementation: Node.js / TypeScript

// src/lib/id.ts
import { uuidv7 } from 'uuidv7';
import { nanoid, customAlphabet } from 'nanoid';
import { createId } from '@paralleldrive/cuid2';

// Database primary key (time-ordered, UUID-compatible)
export const generateRecordId = (): string => uuidv7();

// URL slug (short, URL-safe)
export const generateSlug = (length = 10): string => nanoid(length);

// Distributed microservice ID (no timestamp exposure)
export const generateServiceId = (): string => createId();

// Cryptographically random UUID v4 (Node.js built-in, no dependencies)
export const generateSecureId = (): string => crypto.randomUUID();

UUID Generator Tool

You can generate and inspect UUIDs with the UUID Generator. It supports both v4 and v7, including bulk generation.


Summary

Use Case Recommended
Primary key on a large write-heavy table UUID v7
Security-sensitive resource IDs UUID v4
Short, URL-friendly IDs NanoID
Distributed microservice ID generation CUID2
Standards compliance required UUID v4 or v7 (RFC 9562)

No single identifier is universally best. The right choice depends on your database engine, scale, security requirements, and whether the ID will appear in user-facing URLs. UUID v7 is increasingly the default choice for new projects that care about both standards compliance and database performance.


References