Skip to Content
Api ReferenceCustom Random

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) => Type

Parameters

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: Uint8Array of specified length

Return Value

Returns a generator function:

(size?: number) => string

Examples

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 email

Random Function Requirements

Interface

Your random function must:

  1. Accept a number parameter (bytes needed)
  2. Return a Uint8Array of that length
  3. 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 size bytes)
  • 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 IDs

Use 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

  1. 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 }
  2. 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

FeaturecustomAlphabetcustomRandom
Alphabet✅ Custom✅ Custom
Random source❌ Fixed (crypto)✅ Custom
Security✅ Always secure⚠️ Depends on your function
Complexity✅ Simple⚠️ Advanced
Use caseMost projectsTesting, 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()

See Also