NanoID Generator
Generate tiny URL-safe unique IDs with customizable alphabet
Alphabet
Size
Count
Click Generate to create NanoIDs
What is NanoID?
NanoID is a tiny, fast, URL-safe random ID generator. By default it produces 21-character strings using a 64-character alphabet (A-Za-z0-9_-), providing approximately 126 bits of entropy — comparable to UUID v4's 122 bits but in a shorter string.
NanoID does not embed a timestamp or any structured data. Every ID is purely random, generated from the operating system's cryptographically secure random number generator (crypto.getRandomValues() in browsers, crypto.randomBytes() in Node.js).
NanoID vs UUID v4
NanoID and UUID v4 are both random ID generators backed by a CSPRNG. They differ in format, length, and ecosystem support:
| Property | NanoID (default) | UUID v4 |
|---|---|---|
| Format | URL-safe alphanumeric + _- | Hyphenated hexadecimal |
| Length | 21 characters (default) | 36 characters |
| Entropy | ~126 bits | 122 bits |
| URL-safe | Yes — no encoding needed | Yes (with hyphens) |
| Alphabet | 64 characters (A-Za-z0-9_-) | 16 characters (0-9a-f) |
| Dependencies | Requires npm package | Native (crypto.randomUUID) |
| Customizable | Yes — length and alphabet | No |
| Standard | None (community library) | RFC 4122 / RFC 9562 |
Choose UUID v4 when interoperability with external systems matters — databases with native UUID columns, APIs expecting UUID format, or logging infrastructure that parses UUIDs. Choose NanoID when you want shorter IDs and control the full stack.
Collision Probability by Size
NanoID's collision probability depends on the ID length and the generation rate. The following table uses the default 64-character alphabet:
| Size (chars) | Possible IDs | Collision safety |
|---|---|---|
| 6 | 64 | ~1 in 4.5B — safe for a few thousand IDs |
| 8 | 64 | ~1 in 4.5T — safe for millions of IDs |
| 11 | 64 | ~1 in 2.8 quadrillion — safe for billions of IDs |
| 16 | 64 | ~1 in 1.2 × 10^19 — safe for trillions of IDs |
| 21 | 64 | ~1 in 10^30 — safe for 100 billion IDs per day for centuries |
| 32 | 64 | Comparable to UUID v4 (122 bits) |
| 36 | 36 | Exceeds UUID v4 |
The default 21-character size is chosen to match UUID v4's collision resistance (~126 bits) while being 41% shorter. For most applications, 21 characters is the right choice.
Custom Alphabets
NanoID's alphabet is fully customizable. The library accepts any string of unique characters as the alphabet and generates IDs using only those characters:
A-Za-z0-9_-A-Za-z0-90-9a-f0-9Important: use nanoid/non-secure only for non-security-sensitive applications (e.g. UI element IDs). For any ID that needs to be unguessable, always use the default secure import.
How NanoID Generates Randomness
NanoID uses the operating system's cryptographically secure pseudo-random number generator (CSPRNG). In browsers this is crypto.getRandomValues(); in Node.js it is crypto.randomFillSync(). This is the same entropy source used for TLS session keys — far stronger than Math.random().
Rejection Sampling (Avoiding Modulo Bias)
A naive approach to generating random characters would be: take a random byte (0–255) and compute byte % alphabetSize. This introduces modulo bias — some characters appear slightly more often than others when the alphabet size does not evenly divide 256.
NanoID eliminates this bias using rejection sampling:
- Determine the smallest power-of-two mask that covers the alphabet size (e.g. for a 64-char alphabet, the mask is 63 = 0b00111111)
- Generate random bytes and apply the mask:
byte & mask - If the masked value is within the alphabet range, use it. Otherwise, discard and try again.
This means some random bytes are discarded, but the result is a perfectly uniform distribution over the alphabet — no character is more likely than any other.
// Pure browser — no npm package needed
function generateNanoid(alphabet, size) {
const mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
const step = Math.ceil((1.6 * mask * size) / alphabet.length)
let id = ''
while (id.length < size) {
const bytes = crypto.getRandomValues(new Uint8Array(step))
for (const byte of bytes) {
const idx = byte & mask
if (idx < alphabet.length) {
id += alphabet[idx]
if (id.length === size) break
}
}
}
return id
}
const URL_SAFE = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
generateNanoid(URL_SAFE, 21) // → "V1StGXR8_Z5jdHi6B-myT"Environment Support
Code Examples
JavaScript / TypeScript
// npm i nanoid
import { nanoid } from 'nanoid'
nanoid() // → "V1StGXR8_Z5jdHi6B-myT" (21 chars, URL-safe)
nanoid(8) // → "Uakgb_J5" (custom size)
// Custom alphabet
import { customAlphabet } from 'nanoid'
const hexId = customAlphabet('0123456789abcdef', 16)
hexId() // → "4f3a1b8c9d2e0f7a"
const numId = customAlphabet('0123456789', 8)
numId() // → "30812894"Browser (CDN)
NanoID can be used directly in the browser via a CDN import. No build step required for quick prototyping.
// Pure browser — no npm package needed
function generateNanoid(alphabet, size) {
const mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
const step = Math.ceil((1.6 * mask * size) / alphabet.length)
let id = ''
while (id.length < size) {
const bytes = crypto.getRandomValues(new Uint8Array(step))
for (const byte of bytes) {
const idx = byte & mask
if (idx < alphabet.length) {
id += alphabet[idx]
if (id.length === size) break
}
}
}
return id
}
const URL_SAFE = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
generateNanoid(URL_SAFE, 21) // → "V1StGXR8_Z5jdHi6B-myT"Python
# pip install nanoid
from nanoid import generate
generate() # → "V1StGXR8_Z5jdHi6B-myT"
generate(size=8) # → "Uakgb_J5"
generate('0123456789abcdef', 16) # custom alphabet + sizeNode.js (CommonJS)
// Node.js — stdlib only, no npm needed
const { randomFillSync } = require('crypto')
function nanoid(alphabet, size) {
const mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
const step = Math.ceil((1.6 * mask * size) / alphabet.length)
let id = ''
while (id.length < size) {
const bytes = randomFillSync(Buffer.alloc(step))
for (const byte of bytes) {
const idx = byte & mask
if (idx < alphabet.length) { id += alphabet[idx]; if (id.length === size) break }
}
}
return id
}