# Nano ID: a 127-byte ID generator for JavaScript, and when to reach for it

> Nano ID replaces UUID v4 with a shorter, URL-safe string built from a hardware random source. This review covers the install paths, the customAlphabet API, the alphabet size limit, and where the trade-offs bite.

**ai/nanoid** — A tiny (118 bytes), secure, URL-friendly, unique string ID generator for JavaScript

- Repository: https://github.com/ai/nanoid
- Website: https://zelark.github.io/nano-id-cc/
- Stars: 26,992 · Forks: 879
- Language: JavaScript
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/ai-nanoid

## What Nano ID actually solves

Most JavaScript projects reach for UUID v4 when they need an identifier for a database row, a session, a file name or a client-side model. That works, but it costs 36 characters, includes hyphens that need escaping in URLs, and on the Node side the built-in crypto.randomUUID is the reference point everyone measures against. Nano ID is a direct answer to that: it produces a 21-character string from the alphabet A-Za-z0-9_-, which the README describes as URL-friendly, and it packs a similar number of random bits into fewer symbols. The README states 126 random bits in Nano ID against 122 in UUID v4, and repeats the standard collision figure that 103 trillion version 4 IDs must be generated for a one in a billion chance of duplication.

The audience is narrow and clear. If you are writing JavaScript that runs in Node.js, in a browser, in React Native, or in a bundler-based front end, and you want IDs that are safe to put in a URL path or a query string without encoding, this is the shape of tool you want. It is not a database key format, it is not a sortable identifier, and it is not a specification. It is a function that returns a string.

## The mechanism: hardware randomness plus a larger alphabet

The security argument in the README rests on two claims. The first is unpredictability: Nano ID does not call Math.random(), which the README calls unsafe. In Node.js it uses the crypto module; in browsers it uses the Web Crypto API. Both are described as hardware random generators, which is why the README says the generator can be used in clusters. The second claim is uniformity. The README points out that random % alphabet is a common mistake in hand-rolled ID generators, because it makes some symbols more likely than others and therefore reduces the number of attempts an attacker needs when brute-forcing. Nano ID uses what the README calls a better algorithm and ships a distribution test image to show the result.

The package layout reflects two build targets. The exports map sends the browser and react-native conditions to index.browser.js and everything else to index.js, with index.d.ts as the type entry. There is a separate subpath, nanoid/non-secure, with its own index.js and index.d.ts, which is the variant to use when you have deliberately decided that unpredictability does not matter for a given ID. The repository also carries a bin/ directory, and package.json declares "bin": "./bin/nanoid.js", which is where the CLI comes from. The README lists a CLI section in its table of contents, so the command-line entry point is a documented part of the project rather than an accident of packaging.

## Installing Nano ID and generating your first ID

The README gives npm as the primary install path. The package is ESM-only in the current layout: package.json sets "type": "module", so a CommonJS require will not be the documented route.

```bash
npm install nanoid
```

After that, the import is a named export. The README's own example assigns the result to a model field and shows the shape of the output.

```js
import { nanoid } from "nanoid";
model.id = nanoid(); //=> "V1StGXR8_Z5jdHi6B-myT"
```

If you want a shorter ID, the size is the first argument. The README warns that this increases collision probability and points at the project's own collision calculator at zelark.github.io/nano-id-cc/ to check whether a given size is safe for your volume.

```js
nanoid(10); //=> "IRFa-VaY2b"
```

For a fixed alphabet and length, customAlphabet returns a configured generator. The README uses a lowercase hex alphabet of 16 symbols and a length of 10.

```js
import { customAlphabet } from "nanoid";
const nanoid = customAlphabet("1234567890abcdef", 10);
model.id = nanoid(); //=> "4f90d13a42"
```

The same customAlphabet call is available from nanoid/non-secure when you do not need the hardware random source. There are two other install routes in the README. JSR is offered as an alternative registry: npx jsr add @sitnik/nanoid, after which imports use @sitnik/nanoid instead of nanoid, and the README says this works in Node.js, Deno and Bun. Deno users can instead run deno add jsr:@sitnik/nanoid. The CDN route loads the module directly from a jsdelivr URL, which the README explicitly does not recommend for production because of lower loading performance.

## The alphabet limit is the sharpest edge in the API

customAlphabet is the most interesting part of the API and also the one with the clearest failure mode. The README states that the alphabet must contain from 1 to 256 symbols. Outside that range, it says, the security of the internal generator algorithm is not guaranteed, and a generator built from an empty alphabet or one with more than 256 symbols can loop forever instead of returning an ID. That is not a soft warning about weaker randomness. It is a description of a non-terminating function, which in a request handler means a hung worker rather than a bad ID.

The practical consequence is that any alphabet assembled at runtime, from configuration, from a locale table, or from user input needs a length check before it reaches customAlphabet. A one-symbol alphabet is technically inside the documented range but produces an ID with zero entropy, which is worth stating plainly because the README's range check does not distinguish between a valid alphabet and a useful one. The second argument, the default size, is also not validated against any notion of safety; the README's advice is to check the size in the collision calculator, which is a manual step, not a runtime guard.

There is a second, quieter constraint in the packaging. The browser and react-native conditions in the exports map both point at index.browser.js. If your build tooling resolves the browser condition in an environment where the Web Crypto API is present but behaves differently than you expect, you are running a different file from the one Node.js runs. That is the intended design, but it means a bug that only reproduces in one target is a real possibility, and the README does not document a way to force the Node build in a browser context.

## Nano ID compared with UUID and with the non-secure variant

The comparison the README draws is with UUID v4 specifically, and it is a fair one to start from. Both are random-based identifiers with comparable collision behaviour, and the README's own numbers put them within a few bits of each other. The differences it names are the alphabet size, which compresses the same entropy into 21 symbols instead of 36, and speed. The benchmark block in the README shows nanoid at roughly 20.4 million ops/sec against crypto.randomUUID at roughly 12.9 million and uuid v4 at roughly 7.9 million. Those are the project's own published figures from node ./test/benchmark.js, not an independent measurement, and the same block shows nanoid for browser at roughly 311 thousand ops/sec, which is a different order of magnitude entirely and worth noticing before assuming the Node number carries over to a front end.

The more useful internal comparison is nanoid against nanoid/non-secure. The non-secure build is a separate subpath with its own types, and the README's benchmark lists it at roughly 2.4 million ops/sec, well below the secure build's 20 million. That is the trade: you give up the hardware random source and you do not get the performance back that you might expect. If you are reaching for non-secure purely as an optimisation, the published numbers do not support that reasoning. The case for it is when the ID is genuinely not security-relevant, such as a short-lived UI key, and you want to be explicit about that in the import path itself.

Outside JavaScript, the README states that Nano ID has been ported to over 20 programming languages, and the related searches show people looking for Python, Java and Flutter versions. Those ports are separate projects with their own maintenance, and nothing in this repository's README describes their behaviour, so the guarantees above should not be assumed to transfer.

## Maintenance, licensing and the cost of upgrading

The repository is not archived, and the last push was on 2026-09-16, five days before this review. The most recent release listed is 3.3.19 from 2026-09-10, with 3.3.18 from 2026-08-07 and 6.0.1 from 2026-08-03. The presence of both 3.3.x and 6.0.x lines in the release list is the thing to watch: package.json declares version 6.0.1, so the 6.x line is current, while 3.3.x releases are still being cut. Anyone on 3.x should confirm which line they intend to track rather than assuming the newest tag number is the successor.

The licence is MIT, stated in both package.json and the repository root. MIT is permissive and imposes no copyleft obligation on your own code, but this is not legal advice; if your organisation has a policy on dependency licences, the file to check is LICENSE at the repository root. The dependency story is unusually clean: the README states there are no dependencies, and the devDependencies in package.json are test, lint and benchmark tooling (size-limit, oxlint, better-node-test and a set of comparison libraries). Upgrading therefore carries little risk of transitive breakage, but the ESM-only "type": "module" setting is the upgrade cost that matters. A project still on CommonJS cannot simply bump the version and keep its require calls.

## Conclusion

Adopt Nano ID when you need short, URL-safe identifiers in JavaScript and can accept a 21-character default whose collision math you have checked with the project's own calculator. Do not adopt it if you need a standardised, sortable or database-native identifier format, or if you need an alphabet larger than 256 symbols, since the README states the internal algorithm's security is not guaranteed outside the 1 to 256 range. Before shipping, verify two things in your own code: that every import path resolves to the build you expect (the package maps index.js to index.browser.js under the browser and react-native conditions), and that any custom alphabet you pass stays inside the documented range.

## FAQ

### What is Nano ID used for?

It generates short, URL-friendly unique string IDs in JavaScript. The README's default output is a 21-character string drawn from A-Za-z0-9_-, and it is used for things like model IDs in a front end or a Node.js service.

### What is the difference between a UUID and a Nano ID?

The README says both carry a similar number of random bits, 126 in Nano ID and 122 in UUID v4, so collision probability is comparable. The differences it names are that Nano ID uses a larger alphabet to fit that entropy into 21 symbols instead of 36, and that it is faster than crypto.randomUUID and uuid/v4.

### How do I install Nano ID?

The README's primary path is npm install nanoid. It also documents JSR via npx jsr add @sitnik/nanoid, which requires changing imports to @sitnik/nanoid, and a CDN import that the README does not recommend for production because of lower loading performance.

### How do I use Nano ID in Node.js?

Import the named export and call it. The README's example is import { nanoid } from "nanoid" followed by nanoid(), which returns a 21-character string. The package is ESM-only, since package.json sets "type": "module".

### How do I use Nano ID in React?

The README lists a React section and the package maps the browser and react-native conditions to index.browser.js, so a bundler will resolve the browser build automatically. Installation is the same npm install nanoid, and the import is the same named export.

### What is the nanoid package?

It is the npm package for Nano ID, a dependency-free JavaScript ID generator. package.json describes it as a tiny, secure, URL-friendly unique string ID generator, licensed MIT, with the default export producing a 21-character ID.

## Sources

- [ai/nanoid on GitHub](https://github.com/ai/nanoid)
- [License: MIT](https://github.com/ai/nanoid/blob/main/LICENSE)
- [Project website](https://zelark.github.io/nano-id-cc/)
- [README](https://github.com/ai/nanoid/blob/main/README.md)
- [Releases](https://github.com/ai/nanoid/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ai-nanoid
