Last Updated: 3/9/2026
Non-Secure API
A faster, smaller variant of Nano ID that uses Math.random() instead of cryptographic random generation.
Overview
Package: nanoid/non-secure
Size: 90 bytes (vs 118 bytes for secure version)
Speed: ~2.2M ops/sec (vs ~3.7M ops/sec for secure version)
Security: ❌ Not cryptographically secure
When to Use
✅ Safe for:
- Development and testing
- Temporary IDs that don’t persist
- Non-sensitive UI state
- Environments without crypto support
- Placeholder data
❌ Never use for:
- Session IDs
- API keys or tokens
- Password reset tokens
- Security-critical identifiers
- Production database primary keys
- Authentication/authorization
API
nanoid()
import { nanoid } from 'nanoid/non-secure'
const id = nanoid()
console.log(id) //=> "Uakgb_J5m9g-0JDMbcJqLJ"
// Custom size
const shortId = nanoid(10)
console.log(shortId) //=> "IRFa-VaY2b"Signature: Same as secure nanoid()
function nanoid(size?: number): stringcustomAlphabet()
import { customAlphabet } from 'nanoid/non-secure'
const nanoid = customAlphabet('1234567890', 10)
console.log(nanoid()) //=> "4926581703"Signature: Same as secure customAlphabet()
function customAlphabet(
alphabet: string,
defaultSize?: number
): (size?: number) => stringExamples
Development/Testing
import { nanoid } from 'nanoid/non-secure'
// Mock data generation
const mockUsers = Array(100).fill(null).map((_, i) => ({
id: nanoid(),
name: `User ${i}`,
email: `user${i}@example.com`
}))Temporary UI State
import { nanoid } from 'nanoid/non-secure'
function TodoList() {
const [todos, setTodos] = useState([])
const addTodo = (text) => {
setTodos([...todos, {
id: nanoid(), // Temporary ID, not persisted
text,
completed: false
}])
}
}Client-Side Only IDs
import { nanoid } from 'nanoid/non-secure'
// IDs that never leave the browser
const formId = nanoid()
const componentId = nanoid()
<form id={formId}>
<input id={componentId} />
</form>Environments Without Crypto
// Fallback for old browsers or restricted environments
let nanoid
try {
({ nanoid } = await import('nanoid'))
} catch {
// Crypto not available, use non-secure
({ nanoid } = await import('nanoid/non-secure'))
}
const id = nanoid()Security Risks
Predictability
Math.random() is not cryptographically secure:
import { nanoid } from 'nanoid/non-secure'
const id1 = nanoid()
const id2 = nanoid()
const id3 = nanoid()
// ❌ An attacker can predict id3 from id1 and id2Why: Math.random() uses a pseudorandom number generator (PRNG) seeded by time. With enough samples, the seed can be determined.
Low Entropy
Math.random() provides only ~52 bits of entropy (vs 126 bits for secure version):
Secure nanoid: 2^126 possible values
Non-secure nanoid: ~2^52 possible values (much easier to brute force)Timing Attacks
Math.random() can be influenced by system time:
// If attacker knows approximate time of ID generation,
// they can narrow down possible values significantlyPerformance
Benchmarks
nanoid (secure) 3,693,964 ops/sec
nanoid/non-secure 2,226,483 ops/secParadox: Non-secure is slower than secure!
Why: The secure version uses pooled random generation (128 IDs per system call), while non-secure calls Math.random() for every character.
Bundle Size
nanoid 118 bytes
nanoid/non-secure 90 bytes (28 bytes smaller)Use case: Bundle size is the main reason to use non-secure, not performance.
Implementation Details
Source Code
// non-secure/index.js
let urlAlphabet =
'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
export let nanoid = (size = 21) => {
let id = ''
let i = size | 0
while (i--) {
// Math.random() instead of crypto
id += urlAlphabet[(Math.random() * 64) | 0]
}
return id
}
export let customAlphabet = (alphabet, defaultSize = 21) => {
return (size = defaultSize) => {
let id = ''
let i = size | 0
while (i--) {
id += alphabet[(Math.random() * alphabet.length) | 0]
}
return id
}
}Key differences from secure version:
- Uses
Math.random()instead ofcrypto.getRandomValues() - No random pool (generates on demand)
- Simpler algorithm (no rejection sampling)
- Smaller code size
Migration
From Non-Secure to Secure
// Before
import { nanoid } from 'nanoid/non-secure'
// After (just change import)
import { nanoid } from 'nanoid'
// Everything else stays the same
const id = nanoid()Note: IDs generated by secure version will be different (different random source), but format is identical.
Gradual Migration
// Use secure for new IDs, keep non-secure for existing code
import { nanoid as secureNanoid } from 'nanoid'
import { nanoid as insecureNanoid } from 'nanoid/non-secure'
// New security-critical code
const sessionId = secureNanoid()
// Legacy non-critical code (to be migrated)
const tempId = insecureNanoid()Best Practices
✅ Do’s
Use for development data:
import { nanoid } from 'nanoid/non-secure'
const mockData = {
id: nanoid(),
name: 'Test User'
}Use for client-side temporary IDs:
const componentId = nanoid() // Never sent to serverDocument the usage:
// SECURITY: Using non-secure IDs for temporary UI state only.
// These IDs never persist or leave the client.
import { nanoid } from 'nanoid/non-secure'❌ Don’ts
Don’t use for session IDs:
// WRONG: Session IDs must be secure
import { nanoid } from 'nanoid/non-secure'
const sessionId = nanoid() // ❌ INSECURE!Don’t use for database primary keys:
// WRONG: Primary keys should be secure
import { nanoid } from 'nanoid/non-secure'
const userId = nanoid() // ❌ PREDICTABLE!
await db.users.insert({ id: userId, ... })Don’t use for tokens:
// WRONG: Tokens must be cryptographically secure
import { nanoid } from 'nanoid/non-secure'
const apiKey = nanoid() // ❌ CAN BE GUESSED!Alternatives
If you need fast, non-secure IDs:
Sequential IDs
let counter = 0
const getId = () => `id-${counter++}`
getId() //=> "id-0"
getId() //=> "id-1"Pros: Faster, smaller, sortable
Cons: Not distributed-system-friendly
UUID v1 (Time-based)
import { v1 as uuidv1 } from 'uuid'
const id = uuidv1()
//=> "6c84fb90-12c4-11e1-840d-7b25c5ee775a"Pros: Sortable by time, no collision
Cons: Leaks timestamp and MAC address
ULID
import { ulid } from 'ulid'
const id = ulid()
//=> "01ARZ3NDEKTSV4RRFFQ69G5FAV"Pros: Sortable, compact, secure
Cons: Larger package size
Related APIs
- nanoid() - Secure version
- customAlphabet() - Secure custom alphabet
- Security - Why security matters