Skip to main content
Not sure which ID format to use? This guide will help you choose the right one based on your requirements.

Quick Decision Matrix

Database Primary Keys

Recommended: UUID v7 or ULIDTime-ordered IDs improve database index performance and query efficiency.

URL Shorteners

Recommended: NanoidCompact, URL-safe characters keep URLs short and readable.

Prevent Enumeration

Recommended: CUID2Non-sequential IDs prevent attackers from guessing valid IDs.

Maximum Compatibility

Recommended: UUID v4Universal standard supported by virtually all databases and systems.

Distributed Systems

Recommended: ULIDSortable with high entropy, perfect for distributed generation.

Session Tokens

Recommended: Nanoid or CUID2Short, secure, and random for session management.

Detailed Comparison

Time-Ordered vs Random

Time-ordered IDs (UUID v7, ULID, KSUID) embed a timestamp and sort naturally by creation time:
import { uuidv7 } from 'uniku/uuid/v7'

const [first, second, third] = [uuidv7(), uuidv7(), uuidv7()]
console.log(first < second && second < third) // true
Benefits of time-ordering:
  • Improved database index performance
  • Natural chronological sorting
  • Can extract creation timestamp
  • Better locality in B-tree indexes
Random IDs (UUID v4, CUID2, Nanoid) provide no time information:
import { uuidv4 } from 'uniku/uuid/v4'

const ids = [uuidv4(), uuidv4(), uuidv4()]
// IDs are random - no predictable ordering
Time-ordered IDs can leak information about creation time and volume. If this is a security concern, use CUID2 or UUID v4 instead.

Characteristics Comparison

FormatTime-OrderedLengthEncodingEntropyBinary Size
UUID v436 charsHex (with dashes)122 bits16 bytes
UUID v736 charsHex (with dashes)74 bits*16 bytes
ULID26 charsCrockford Base3280 bits*16 bytes
CUID224 chars**Base36HighNo binary format
Nanoid21 chars**URL-safe Base64126 bitsNo binary format
KSUID27 charsBase62128 bits*20 bytes
* Time-ordered IDs allocate bits to timestamp, reducing random entropy. The entropy listed is for the random portion only.** CUID2 and Nanoid support custom lengths.

Use Case Recommendations

Database Primary Keys

Use UUID v7 or ULID for optimal performance:
import { uuidv7 } from 'uniku/uuid/v7'

// In your database model
interface User {
  id: string // UUID v7
  email: string
  createdAt: Date
}

const user = {
  id: uuidv7(),
  email: '[email protected]',
  createdAt: new Date()
}
Why time-ordered? Most databases use B-tree indexes. Time-ordered IDs insert at the end of the index, avoiding expensive page splits and fragmentation.
UUID v7 is ideal when:
  • You need RFC-standard compliance
  • Your database has native UUID type support
  • You want to extract timestamps: uuidv7.timestamp(id)
ULID is ideal when:
  • You want shorter string representation (26 vs 36 characters)
  • You prefer case-insensitive encoding
  • You’re using it in URLs or file names

URL Shorteners and Public IDs

Use Nanoid for compact, URL-friendly identifiers:
import { nanoid } from 'uniku/nanoid'

// Short URL
const shortUrl = `https://example.com/${nanoid(8)}`
// => https://example.com/V1StGXR8

// Invite code
const inviteCode = nanoid(12)
// => IRFa-VaY2bQj
Benefits:
  • Compact (21 characters by default)
  • URL-safe (no encoding needed)
  • Customizable length and alphabet
  • Fast generation

Preventing Enumeration Attacks

Use CUID2 when you need to prevent ID guessing:
import { cuid2 } from 'uniku/cuid2'

// API keys or tokens
const apiKey = cuid2()
// => pfh0haxfpzowht3oi213cqos

// Invoice numbers (not sequential)
const invoiceId = cuid2({ length: 16 })
// => tz4a98xxat4eqr3z
Avoid time-ordered IDs if:
  • You don’t want to leak creation time
  • You want to prevent volume estimation
  • You need to hide sequential patterns

Distributed Systems

Use ULID or UUID v7 for collision-free distributed generation:
import { ulid } from 'uniku/ulid'

// Each microservice can generate IDs independently
const orderId = ulid()
const paymentId = ulid()

// IDs sort correctly even when generated on different servers
Why they work well:
  • No coordination needed between nodes
  • Timestamp prefix ensures global ordering
  • High entropy prevents collisions
  • Can merge and sort logs from multiple sources

Maximum Compatibility

Use UUID v4 when compatibility is paramount:
import { uuidv4 } from 'uniku/uuid/v4'

const id = uuidv4()
// => 550e8400-e29b-41d4-a716-446655440000
Best for:
  • Legacy systems requiring RFC 4122 compliance
  • Databases with native UUID type (Postgres, MySQL)
  • When time-ordering isn’t needed
  • Maximum interoperability

Performance Considerations

Generation speed varies significantly:
GeneratorPerformance
ULID85× faster than npm ulid
CUID28× faster than @paralleldrive/cuid2
KSUID1.5× faster than @owpz/ksuid
UUID v71.1× faster than uuid@v7
Nanoid~comparable to nanoid
UUID v4npm uuid is 1.1× faster
See the Performance Guide for detailed benchmarks.

Migration Paths

Switching from another ID library? Check the Migration Guide for:
  • Drop-in replacement instructions
  • API differences to be aware of
  • Breaking changes
  • Compatibility notes

Decision Flowchart

Summary

ScenarioBest ChoiceSecond Choice
Database primary keysUUID v7ULID
URLs and short codesNanoidCUID2
Security tokensCUID2Nanoid
Legacy compatibilityUUID v4UUID v7
Distributed systemsULIDUUID v7
File namesULIDNanoid
Session IDsNanoidCUID2
Invoice numbersCUID2UUID v4
Still unsure? Start with UUID v7 for database IDs and Nanoid for user-facing identifiers. Both are excellent general-purpose choices.

Build docs developers (and LLMs) love