CUID2 Generator
Generate collision-resistant unique IDs that always start with a letter — safe everywhere
Generate CUID2 Identifiers
Default 24. Longer = less collision risk.
CUID2 Properties
Uses SHA-256 hash of timestamp + counter + randomness. Not sequential — no information leaks.
Counter-based entropy source makes brute-force enumeration infeasible even if some inputs are known.
Lowercase alphanumeric only. Always starts with a letter — safe as HTML IDs, class names, and URL segments.
Unlike ULID/UUID v7, CUID2 has no timestamp prefix. Best when you don't need sort-by-creation.
CUID2 vs UUID v4 vs ULID
| Feature | CUID2 | UUID v4 | ULID |
|---|---|---|---|
| Length | 24 | 36 | 26 |
| URL-safe | Yes | With care | Yes |
| Time-ordered | No | No | Yes |
| Always letter-start | Yes | No | No |
| Standard | Community | RFC 9562 | Community |
You need a unique identifier that is safe for public URLs, works as an HTML element ID, never starts with a number, and reveals absolutely nothing about when or how many records have been created. UUID v4 starts with a hex digit, ULID exposes creation time, and auto-increment is out of the question for security-sensitive contexts. CUID2 was designed precisely for this scenario.
What Is CUID2?
CUID2 is the second generation of the Collision-Resistant Unique IDentifier format, developed by Eric Elliott and the ParallelDrive team. CUID2 addresses weaknesses in the original CUID1 format — specifically, CUID1’s sequential counter made IDs partially predictable.
CUID2 generates identifiers that are:
- Always lowercase alphanumeric (
a-z,0-9) - Always start with a lowercase letter (
a-z) - Configurable length (4–32 characters, default 24)
- Collision-resistant — uses SHA-256 hash combining timestamp + counter + random data
- Non-sequential — the hash output hides all input structure
A typical CUID2 looks like: clh3ux4f90001vkge5gq2g77d
Why “Always Starts With a Letter” Matters
Most ID formats can start with a digit. This is a problem in several contexts:
- HTML IDs and CSS selectors: An
idattribute starting with a digit is technically invalid in HTML4 and requires escaping in CSS selectors (#\33 abcforid="3abc"). - Programming language identifiers: Variable names and object keys cannot start with a digit in most languages without quoting.
- URL path segments: While digits are valid in URLs, some routing libraries or conventions expect path segments to start with a letter.
CUID2 solves this by always placing a random lowercase letter as the first character, making IDs safe for use everywhere without any special handling.
How CUID2 Generation Works
CUID2 uses a deterministic hash construction rather than pure randomness:
- Timestamp component: The current Unix timestamp in milliseconds, encoded in base36.
- Counter component: A session counter, incremented on each call and encoded in base36. This ensures uniqueness when multiple IDs are generated within the same millisecond.
- Fingerprint component: A value derived from session-specific data (browser user agent, runtime properties). This differentiates IDs generated by different clients at the same time.
- Random component: 32 bytes from
crypto.getRandomValues(). - SHA-256 hash: All components are concatenated and hashed with SHA-256. The hash output is encoded in base36 and truncated to the desired length.
- Prefix: A random lowercase letter is prepended.
The SHA-256 hash ensures that the output has no statistical correlation to any of the inputs. Even if two clients generate IDs with nearly identical inputs, the outputs are computationally indistinguishable.
CUID2 vs UUID v4 vs ULID
| Feature | CUID2 | UUID v4 | ULID |
|---|---|---|---|
| Default length | 24 | 36 | 26 |
| Alphabet | a-z, 0-9 | hex + hyphens | Crockford Base32 |
| Starts with letter | Always | Never | Sometimes |
| Time-ordered | No | No | Yes |
| Timestamp leakage | No | No | Yes |
| URL-safe | Yes | With encoding | Yes |
| Standard | Community | RFC 9562 | Community |
| Hash-based | Yes | No | No |
When to Use CUID2
Public-facing resource identifiers: When an ID appears in a URL or API response, you may not want it to reveal creation time or record count. CUID2 exposes neither — the SHA-256 hash obscures all structural information.
HTML element IDs: CUID2 is guaranteed safe as an id attribute value — lowercase alphanumeric, starts with a letter, no special characters.
Short codes with safety: With a length of 10, CUID2 provides adequate collision resistance for databases with up to tens of millions of records.
Security-sensitive contexts: When you need an identifier that resists reverse-engineering, CUID2’s hash construction provides preimage resistance — knowing the output reveals nothing about the inputs.
Generating CUID2 in Code
// npm install @paralleldrive/cuid2
import { createId } from '@paralleldrive/cuid2';
// Default length (24)
const id = createId();
// "clh3ux4f90001vkge5gq2g77d"
// Custom length
import { init } from '@paralleldrive/cuid2';
const cuid2 = init({ length: 10 });
const shortId = cuid2();
// "clh3ux4f90"
// Validation
import { isCuid } from '@paralleldrive/cuid2';
isCuid("clh3ux4f90001vkge5gq2g77d"); // true
# pip install cuid2
from cuid2 import cuid_wrapper
cuid = cuid_wrapper()
id = cuid()
# "clh3ux4f90001vkge5gq2g77d"
// TypeScript with validation
import { createId, isCuid } from '@paralleldrive/cuid2';
interface Record {
id: string;
name: string;
}
const record: Record = {
id: createId(),
name: "Example",
};
Collision Resistance at Scale
The mathematical collision resistance of CUID2 depends on the length:
| Length | Safe generation count (1% collision probability) |
|---|---|
| 10 | ~1 billion |
| 16 | ~4 trillion |
| 24 (default) | ~4.2 × 10^17 (420 quadrillion) |
| 32 | ~7.3 × 10^23 |
For a database with 100 million records, length 10 is mathematically sufficient. The default length of 24 provides an enormous safety margin suitable for any realistic scale.
What CUID2 Does Not Solve
CUID2 is not suitable when you need:
- Time-ordered identifiers for database indexes: Use ULID or UUID v7 instead. CUID2’s non-sequential nature causes the same B-tree fragmentation as UUID v4.
- Deduplication with timestamps: CUID2 does not embed recoverable timestamps. Use ULID if you need to extract creation time from the ID.
- Compatibility with UUID columns: Most databases have native UUID types. CUID2 requires a VARCHAR/TEXT column.
Privacy
All CUID2 generation runs in your browser using the Web Crypto API (crypto.getRandomValues() and crypto.subtle.digest()). No data is sent to any server. The fingerprint component uses only client-side information that never leaves your browser.
Frequently Asked Questions
Is CUID2 better than UUID v4? It depends on your requirements. CUID2 is better when you need: always-starts-with-a-letter, URL-safe without encoding, or configurable length. UUID v4 is better when you need: database-native support (UUID type), RFC standardization, or time-ordering (UUID v7).
Can CUID2 collide? Collisions are theoretically possible but practically negligible at default length 24. The birthday paradox probability for 24-character base36 strings is astronomically small — you would need to generate more IDs than atoms in the observable universe before collision became a concern.
Why does CUID2 use a hash instead of raw random bytes? Raw randomness alone does not prevent the birthday paradox as efficiently as a well-constructed hash that combines multiple entropy sources. The counter component ensures uniqueness even under identical conditions, and the hash prevents statistical correlation between sequential IDs.
Is CUID2 compatible with the original CUID?
No. CUID2 is a completely new format and not backward compatible with CUID1. CUID1 IDs start with c followed by a timestamp-based prefix. CUID2 IDs start with a random letter and have no recognizable structure.