nanoid
Nano ID generates short, URL-safe, cryptographically random string IDs. The default call returns 21 characters from the A-Za-z0-9_- alphabet, packing about the same randomness as a UUID v4 (126 vs 122 bits) into 15 fewer characters. It has zero dependencies, the README documents the core at 118 bytes minified and brotlied, and it reads randomness from the Web Crypto API or Node's crypto module rather than Math.random. A large slice of its downloads is transitive: PostCSS depends on the 3.x line, which the author still maintains alongside v6.
The best pick when you specifically want short, random, URL-safe IDs and control over the alphabet; it is tiny, fast, and impeccably maintained. If a plain UUID works, crypto.randomUUID costs nothing, and if IDs become database primary keys at scale, prefer a sortable scheme instead.
Use it if
- You need short IDs for URLs, slugs, or user-facing references where a 36-character UUID is ugly and the 21-character default (or shorter, at your own risk) fits
- You want to tune the tradeoff: customAlphabet lets you set exactly which characters and how many, with a published collision calculator to check your choice
- You generate IDs in the browser or edge runtimes and want something tiny that uses Web Crypto correctly, including uniform distribution instead of the biased modulo trick
- You need deterministic IDs in tests: customRandom accepts a seeded generator so snapshots stay stable
- Your codebase is CommonJS: nanoid has been ESM-only since v4, so require('nanoid') pins you to the 3.x line forever (still patched, but frozen in features)
- You run Node older than 22: v6 declares engines ^22 || ^24 || >=26 and drops Node 18 and 20; you would be installing the older v5 line on day one
- You need database-friendly ordered IDs: random IDs scatter inserts across a B-tree index, so for primary keys at scale a time-sortable scheme (UUIDv7, ULID) is the better default
- A built-in is enough: crypto.randomUUID() ships in Node and every modern browser with zero dependencies; if you do not care about ID length or alphabet, you may not need a package at all
Setup reality
npm install nanoid and one import; there is no config and types are bundled. The friction is environmental: ESM-only means CJS projects need dynamic import or the old 3.x line, Jest setups without proper ESM support choke on it, and v6's engines field will refuse Node 20 in CI images that have not been bumped. React Native needs the react-native-get-random-values polyfill imported before nanoid. Also do not benchmark-shop the non-secure build: since the v6 speedups the secure default is the faster one.
Patterns
Generate a default 21-character IDgenerate-id
import { nanoid } from 'nanoid'
const id = nanoid() //=> "V1StGXR8_Z5jdHi6B-myT"IDs come from a hardware random source and can start with - or _, which matters for systems with reserved prefixes.
Generate a shorter or longer IDcustom-length
import { nanoid } from 'nanoid'
nanoid(10) //=> "IRFa-VaY2b"
nanoid(32) // longer, lower collision oddsShorter means more collisions: check the nano-id-cc calculator before shipping anything under the default 21 characters.
Restrict the alphabetcustom-alphabet
import { customAlphabet } from 'nanoid'
const hexId = customAlphabet('1234567890abcdef', 12)
hexId() //=> "4f90d13a42bf"
const orderNo = customAlphabet('346789ABCDEFGHJKLMNPQRTUVWXY', 8)Removing look-alike characters (0/O, 1/l/I) is the common use; the nanoid-dictionary package has prebuilt alphabets. Max 256 symbols.
Deterministic IDs with a seeded generatorseeded-ids-for-tests
import { customRandom, urlAlphabet } from 'nanoid'
import seedrandom from 'seedrandom'
const rng = seedrandom('test-seed')
const testId = customRandom(urlAlphabet, 21, size =>
new Uint8Array(size).map(() => 256 * rng())
)
testId() // same sequence every runThe project warns that the internal call sequence may change between versions, so seeded output is only stable while you pin the nanoid version.
Use the non-secure build where crypto is missingnon-secure-fallback
import { nanoid } from 'nanoid/non-secure'
const id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqLJ"Despite the name this is not a speed hack: the README benchmark shows it far slower than the secure default. Use it only in environments without a hardware random source.
Do not use nanoid() for React keysreact-keys-antipattern
// wrong: new key every render, remounts every item
todos.map(todo => <li key={nanoid()}>{todo.text}</li>)
// right: use a stable id stored on the data
todos.map(todo => <li key={todo.id}>{todo.text}</li>)Keys must be stable across renders. For linking labels to inputs, React's built-in useId() is the tool, not nanoid.
Use in React Native and Exporeact-native-setup
import 'react-native-get-random-values'
import { nanoid } from 'nanoid'
const id = nanoid()React Native has no built-in crypto; the polyfill import must come before the nanoid import or you get a runtime error.
Prefix IDs for PouchDB/CouchDBcouchdb-safe-ids
import { nanoid } from 'nanoid'
await db.put({
_id: 'doc-' + nanoid(),
title: 'hello'
})CouchDB reserves IDs starting with an underscore, and nanoid can legitimately generate one; always prefix.
Brand IDs as opaque types in TypeScripttypescript-branded-ids
declare const userIdBrand: unique symbol
type UserId = string & { [userIdBrand]: true }
const id = nanoid<UserId>()
interface User {
id: UserId
name: string
}The generic parameter casts the returned string to your branded type, so a UserId cannot be passed where an OrderId is expected.
Generate IDs from the terminalcli-generate
$ npx nanoid
LZfXLFzPPR4NNrgjlWDxn
$ npx nanoid --size 10
L3til0JS4z
$ npx nanoid --alphabet abc --size 15
bccbcabaabaccab--alphabet requires --size to be set explicitly; handy for one-off tokens without writing a script.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| uuid | npm | You need standard RFC UUIDs (v4, v7) for interoperability with databases and other systems that expect the format. |
| ulid | npm | You want lexicographically sortable IDs with an embedded timestamp for pagination and index locality. |
| @paralleldrive/cuid2 | npm | You want collision-resistant IDs designed to avoid leaking any timing information, at some length cost. |