Last Updated: 3/9/2026
customRandom()
Create a custom ID generator with your own alphabet and random bytes generator.
Signature
function customRandom<Type extends string>(
alphabet: string,
size: number,
random: (bytes: number) => Uint8Array
): (size?: number) => TypeParameters
alphabet (required)
- Type:
string - Constraints: Must be ≤256 characters
- Description: Characters to use for ID generation
size (required)
- Type:
number - Description: Default length of generated IDs
random (required)
- Type:
(bytes: number) => Uint8Array - Description: Function that returns random bytes
- Parameters:
bytes- number of random bytes needed - Return:
Uint8Arrayof specified length
Return Value
Returns a generator function:
(size?: number) => stringExamples
Seed-Based Generator
import { customRandom } from 'nanoid'
import seedrandom from 'seedrandom'
const seed = 'my-secret-seed'
const rng = seedrandom(seed)
const nanoid = customRandom('abcdef', 10, size => {
return new Uint8Array(size).map(() => Math.floor(256 * rng()))
})
console.log(nanoid()) //=> "fbaefaadeb"
console.log(nanoid()) //=> "cdaebfcdfa"
// Same seed produces same sequence
const rng2 = seedrandom(seed)
const nanoid2 = customRandom('abcdef', 10, size => {
return new Uint8Array(size).map(() => Math.floor(256 * rng2()))
})
console.log(nanoid2()) //=> "fbaefaadeb" (same as first)Using Default Random
import { customRandom, random } from 'nanoid'
const nanoid = customRandom('0123456789', 10, random)
console.log(nanoid()) //=> "4926581703"With urlAlphabet
import { customRandom, urlAlphabet, random } from 'nanoid'
const nanoid = customRandom(urlAlphabet, 21, random)
console.log(nanoid()) //=> "V1StGXR8_Z5jdHi6B-myT"Custom Entropy Source
import { customRandom } from 'nanoid'
import { getEntropyFromHardware } from './hardware'
const nanoid = customRandom('0123456789ABCDEF', 16, size => {
const bytes = new Uint8Array(size)
for (let i = 0; i < size; i++) {
bytes[i] = getEntropyFromHardware()
}
return bytes
})
console.log(nanoid()) //=> "A4F9D13E42B8C7E1"Use Cases
Reproducible IDs for Testing
import { customRandom } from 'nanoid'
import seedrandom from 'seedrandom'
// Test helper
function createTestIdGenerator(seed) {
const rng = seedrandom(seed)
return customRandom('0123456789abcdef', 16, size => {
return new Uint8Array(size).map(() => Math.floor(256 * rng()))
})
}
// Tests always produce same IDs
test('user creation', () => {
const nanoid = createTestIdGenerator('test-seed')
const user = createUser({ id: nanoid() })
expect(user.id).toBe('a4f9d13e42b8c7e1') // Predictable
})Custom Hardware RNG
import { customRandom } from 'nanoid'
import { readFromTPM } from './tpm'
// Use Trusted Platform Module for extra security
const nanoid = customRandom('A-Za-z0-9_-', 21, size => {
return readFromTPM(size) // Hardware security module
})
const secureToken = nanoid()Deterministic Migration
import { customRandom } from 'nanoid'
import crypto from 'crypto'
// Generate same IDs for same input (for migrations)
function deterministicId(input) {
const hash = crypto.createHash('sha256').update(input).digest()
let offset = 0
const nanoid = customRandom('0123456789abcdef', 16, size => {
const bytes = new Uint8Array(size)
for (let i = 0; i < size; i++) {
bytes[i] = hash[offset++ % hash.length]
}
return bytes
})
return nanoid()
}
console.log(deterministicId('user@example.com'))
//=> Always same ID for same emailRandom Function Requirements
Interface
Your random function must:
- Accept a
numberparameter (bytes needed) - Return a
Uint8Arrayof that length - Fill array with random values (0-255)
function myRandom(size) {
const bytes = new Uint8Array(size)
// Fill with random values
for (let i = 0; i < size; i++) {
bytes[i] = Math.floor(Math.random() * 256)
}
return bytes
}Validation
⚠️ Nano ID does NOT validate your random function. Ensure:
- Returns
Uint8Array(not regular array) - Correct length (exactly
sizebytes) - Values in 0-255 range
- Sufficient entropy for your use case
Security Warnings
⚠️ Non-Determinism Not Guaranteed
// INSECURE: Seeded generators are predictable
const rng = seedrandom('known-seed')
const nanoid = customRandom('abcdef', 10, size => {
return new Uint8Array(size).map(() => Math.floor(256 * rng()))
})
// ❌ Anyone with the seed can predict all IDsUse only for:
- Testing
- Deterministic migrations
- Non-security-critical IDs
Never use for:
- Session IDs
- API tokens
- Security tokens
- Password reset tokens
⚠️ Sequence May Change Between Versions
Nano ID may change how it calls your random function:
// Version 5.0 might call:
random(21) // Once
// Version 6.0 might call:
random(10) // Multiple times
random(11)Implication: Same seed may produce different IDs across Nano ID versions.
⚠️ Use Crypto Random for Security
For production security-critical IDs:
import { customRandom, random } from 'nanoid'
// ✅ SECURE: Uses hardware random
const nanoid = customRandom(alphabet, size, random)
// ❌ INSECURE: Math.random() is predictable
const nanoid = customRandom(alphabet, size, size => {
return new Uint8Array(size).map(() => Math.floor(Math.random() * 256))
})Performance
Overhead
customRandom() performance depends entirely on your random function:
With crypto random: ~3.7M ops/sec
With Math.random(): ~2.2M ops/sec
With slow custom RNG: Varies (measure it!)Optimization Tips
-
Cache random bytes if possible:
let pool = new Uint8Array(1024) let offset = 0 function pooledRandom(size) { if (offset + size > pool.length) { crypto.getRandomValues(pool) offset = 0 } const bytes = pool.slice(offset, offset + size) offset += size return bytes } -
Avoid allocations in hot paths:
// ❌ Slow: Creates new array every time const nanoid = customRandom(alphabet, size, size => { return new Uint8Array(size).map(() => Math.floor(Math.random() * 256)) }) // ✅ Faster: Reuse buffer let buffer = new Uint8Array(256) const nanoid = customRandom(alphabet, size, size => { if (buffer.length < size) buffer = new Uint8Array(size) for (let i = 0; i < size; i++) { buffer[i] = Math.floor(Math.random() * 256) } return buffer.slice(0, size) })
Comparison with customAlphabet
| Feature | customAlphabet | customRandom |
|---|---|---|
| Alphabet | ✅ Custom | ✅ Custom |
| Random source | ❌ Fixed (crypto) | ✅ Custom |
| Security | ✅ Always secure | ⚠️ Depends on your function |
| Complexity | ✅ Simple | ⚠️ Advanced |
| Use case | Most projects | Testing, custom hardware |
Recommendation: Use customAlphabet() unless you specifically need custom random generation.
Common Mistakes
❌ Returning regular array
// WRONG: Returns Array, not Uint8Array
const nanoid = customRandom('abcdef', 10, size => {
return Array(size).fill(0).map(() => Math.floor(Math.random() * 256)) // ❌
})✅ Correct:
const nanoid = customRandom('abcdef', 10, size => {
return new Uint8Array(size).map(() => Math.floor(Math.random() * 256)) // ✅
})❌ Wrong byte range
// WRONG: Returns 0-1 instead of 0-255
const nanoid = customRandom('abcdef', 10, size => {
return new Uint8Array(size).map(() => Math.random()) // ❌ 0-1
})✅ Correct:
const nanoid = customRandom('abcdef', 10, size => {
return new Uint8Array(size).map(() => Math.floor(Math.random() * 256)) // ✅ 0-255
})❌ Using for security without crypto
// WRONG: Math.random() for session IDs
const nanoid = customRandom('A-Za-z0-9', 21, size => {
return new Uint8Array(size).map(() => Math.floor(Math.random() * 256)) // ❌ INSECURE
})
const sessionId = nanoid() // ❌ Predictable!✅ Correct:
import { customRandom, random } from 'nanoid'
const nanoid = customRandom('A-Za-z0-9', 21, random) // ✅ Secure
const sessionId = nanoid()Related APIs
- nanoid() - Default secure generator
- customAlphabet() - Custom alphabet only
- random() - Default random bytes function
See Also
- Security - Cryptographic properties
- How It Works - Algorithm details
- Testing - Reproducible IDs for tests