Skip to content

Instantly share code, notes, and snippets.

@m0o0scar
Created August 8, 2024 00:45
Show Gist options
  • Save m0o0scar/d72c0ff4191bc36ec3ee09ae2785cc7f to your computer and use it in GitHub Desktop.
Save m0o0scar/d72c0ff4191bc36ec3ee09ae2785cc7f to your computer and use it in GitHub Desktop.
ai/nanoid. Continue this conversation at https://readfm.vercel.app?gist=d72c0ff4191bc36ec3ee09ae2785cc7f

[github] ai/nanoid

Source

JavaScript / 1.8K lines of code. A tiny (124 bytes), secure, URL-friendly, unique string ID generator for JavaScript

URL: https://github.com/ai/nanoid

Conversation

▲ Explain the major function of the project and how it works by referencing the most important and relevant code snippets.

The nanoid project is a lightweight, secure, and URL-friendly unique string ID generator for JavaScript. It aims to provide a more efficient and compact alternative to UUIDs.

Here's how it works:

1. Secure Randomness:

  • index.js: The core of the project relies on the crypto module in Node.js and the Web Crypto API in browsers to generate secure random bytes.
    import { webcrypto as crypto } from 'node:crypto'
    // ...
    export function random(bytes) {
      // ...
      crypto.getRandomValues(pool)
      // ...
    }
  • This ensures that the generated IDs are unpredictable and resistant to collision attacks.

2. Custom Alphabet and Size:

  • index.js: The customAlphabet function allows users to define their own alphabet and ID size.
    export function customAlphabet(alphabet, size = 21) {
      return customRandom(alphabet, size, random)
    }
  • This provides flexibility for specific use cases where a custom alphabet or ID length is required.

3. Custom Random Generator:

  • index.js: The customRandom function allows users to replace the default random byte generator with their own custom function.
    export function customRandom(alphabet, defaultSize, getRandom) {
      // ...
      return (size = defaultSize) => {
        // ...
        let bytes = getRandom(step)
        // ...
      }
    }
  • This enables advanced scenarios where users need to control the randomness generation process.

4. ID Generation:

  • index.js: The nanoid function generates a unique ID using the default alphabet (urlAlphabet) and a specified size.
    export function nanoid(size = 21) {
      // ...
      for (let i = poolOffset - size; i < poolOffset; i++) {
        id += scopedUrlAlphabet[pool[i] & 63]
      }
      return id
    }
  • It utilizes a bitmask to ensure that the generated ID stays within the bounds of the alphabet.

5. Non-Secure API:

  • non-secure/index.js: The project also provides a non-secure API for situations where security is not a primary concern.
    export let nanoid = (size = 21) => {
      let id = ''
      // ...
      id += urlAlphabet[(Math.random() * 64) | 0]
      // ...
    }
  • This API uses the Math.random() function, which is less secure and prone to collisions.

In summary, the nanoid project provides a robust and efficient way to generate unique IDs in JavaScript. It prioritizes security and offers flexibility through custom alphabet and random generator options. The non-secure API caters to scenarios where security is not a critical factor.

Data Source Content

Full data source content here Project: ai/nanoid

URL: https://github.com/ai/nanoid

Source tree:

ai/nanoid/blob/main
├── CHANGELOG.md
├── README.id-ID.md
├── README.md
├── README.ru.md
├── README.zh-CN.md
├── eslint.config.js
├── index.browser.js
├── index.d.ts
├── index.js
├── nanoid.js
├── non-secure
|  ├── index.d.ts
|  └── index.js
├── package.json
└── url-alphabet/index.js

ai/nanoid/blob/main/CHANGELOG.md:

# Change Log
This project adheres to [Semantic Versioning](http://semver.org/).

## 5.0.7
* Fixed Parcel support (by @WilhelmYakunin).

## 5.0.6
* Fixed React Native support.

## 5.0.5
* Make browser’s version faster by increasing size a little (by Samuel Elgozi).

## 5.0.4
* Fixed CLI docs (by @ilyaboka).

## 5.0.3
* Fixed CLI docs (by Chris Schmich).

## 5.0.2
* Fixed `webcrypto` import (by Divyansh Singh).

## 5.0.1
* Fixed Node.js 18 support.

## 5.0
* Moved Node.js version to Web Crypto API.
* Removed async API since Web Crypto API has only sync version.
* Removed Node.js 14 and 16 support.

## 4.0.2
* Added [link](https://github.com/sponsors/ai) to Github Sponsors.

## 4.0.1
* Reduced npm package size (by @HiChen404).

## 4.0
* Removed CommonJS support. Nano ID 4 will work only with ESM applications.
  We will support 3.x branch with CommonJS for users who can’t migrate to ESM.
* Removed Node.js 10 and Node.js 12 support.
* Reduced npm package size.

## 3.3.7
* Fixed `node16` TypeScript support (by Saadi Myftija).

## 3.3.6
* Fixed package.

## 3.3.5
* Backport funding information.

## 3.3.4
* Fixed `--help` in CLI (by @Lete114).

## 3.3.3
* Reduced size (by Anton Khlynovskiy).

## 3.3.2
* Fixed `enhanced-resolve` support.

## 3.3.1
* Reduced package size.

## 3.3
* Added `size` argument to function from `customAlphabet` (by Stefan Sundin).

## 3.2
* Added `--size` and `--alphabet` arguments to binary (by Vitaly Baev).

## 3.1.32
* Reduced `async` exports size (by Artyom Arutyunyan).
* Moved from Jest to uvu (by Vitaly Baev).

## 3.1.31
* Fixed collision vulnerability on object in `size` (by Artyom Arutyunyan).

## 3.1.30
* Reduced size for project with `brotli` compression (by Anton Khlynovskiy).

## 3.1.29
* Reduced npm package size.

## 3.1.28
* Reduced npm package size.

## 3.1.27
* Cleaned `dependencies` from development tools.

## 3.1.26
* Improved performance (by Eitan Har-Shoshanim).
* Reduced npm package size.

## 3.1.25
* Fixed `browserify` support.

## 3.1.24
* Fixed `browserify` support (by Artur Paikin).

## 3.1.23
* Fixed `esbuild` support.

## 3.1.22
* Added `default` and `browser.default` to `package.exports`.

## 3.1.21
* Reduced npm package size.

## 3.1.20
* Fix ES modules support.

## 3.1.19
* Reduced `customAlphabet` size (by Enrico Scherlies).

## 3.1.18
* Fixed `package.exports`.

## 3.1.17
* Added files without `process`.

## 3.1.16
* Speeded up Nano ID 4 times (by Peter Boyer).

## 3.1.15
* Fixed `package.types` path.

## 3.1.14
* Added `package.types`.

## 3.1.13
* Removed Node.js 15.0.0 with `randomFillSync` regression from `engines.node`.

## 3.1.12
* Improved IE 11 docs.

## 3.1.11
* Fixed asynchronous `customAlphabet` in browser (by @LoneRifle).

## 3.1.10
* Fix ES modules support.

## 3.1.9
* Try to fix React Native Expo support.

## 3.1.8
* Add React Native Expo support.

## 3.1.7
* Clean up code.

## 3.1.6
* Avoid `self` using.

## 3.1.5
* Improve IE docs and warning.

## 3.1.4
* Restrict old Node.js 13 by `engines.node` (by Cansin Yildiz).

## 3.1.3
* Fix ES modules issue with CLI.

## 3.1.2
* Add shebang to CLI.

## 3.1.1
* Fix CLI.

## 3.1
* Add `npx nanoid` CLI.

## 3.0.2
* Fix docs (by Dylan Irlbeck ).

## 3.0.1
* Fix React Native warning on `non-secure` import (by Jia Huang).

## 3.0
**Migration guide:** <https://github.com/ai/nanoid/releases/tag/3.0.0>
* Move to ES2016 syntax. You need to use Babel for IE 11.
* Move to named exports `import { nanoid } from 'nanoid'`.
* Move `import url from 'nanoid/url'` to `import { urlAlphabet } from 'nanoid'`.
* Replace `format()` to `customRandom()`.
* Replace `generate()` to `customAlphabet()`.
* Remove `async/format`.
* Remove React Native support for `nanoid/async`.
* Add `nanoid.js` to use directly in browser from CDN.
* Add TypeScript type definitions.
* Add ES modules support for bundlers, Node.js, and React Native.
* Fix React Native support.
* Reduce size.
* Improve docs (by Dair Aidarkhanov).

## 2.1.11
* Reduce size (by Anton Evzhakov).

## 2.1.10
* Reduce size by 10% (by Anton Khlynovskiy).

## 2.1.9
* Reduce `format` and `async/format` size (by Dair Aidarkhanov).

## 2.1.8
* Improve React docs (by Nahum Zsilva).

## 2.1.7
* Reduce `index`, `async` and `non-secure` size (by @polemius).

## 2.1.6
* Reduce size (by Stas Lashmanov).
* Return fast mask for Node.js.

## 2.1.5
* Reduce size (by Max Graey).
* Fix IE support.

## 2.1.4
* Reduce `generate` size (by Vsevolod Rodionov).
* Reduce `format` and `format` size (by Victor).
* Reduce `async`, `non-secure` and `non-secure/generate` size.
* Speed up `format` and `async/format` (by Max Graey).
* Improve development process on Windows (by Stanislav Lashmanov).

## 2.1.3
* Improve performance (by Stephen Richardson).
* Reduce size (by Stephen Richardson).

## 2.1.2
* Improve docs.

## 2.1.1
* Fix React Native support (by Shawn Hwei).

## 2.1
* Improve React Native support (by Sebastian Werner).

## 2.0.4
* Improve error text for React Native (by Sebastian Werner).

## 2.0.3
* Fix freeze on string in ID length.

## 2.0.2
* Improve docs (by Sylvanus Kateile and Mark Stosberg).

## 2.0.1
* Reduce npm package size.
* Mark package as not having side effects (by @xiaody).

## 2.0
* Use `-` instead of `~` in default alphabet to by file name safe.
* Add `nanoid/non-secure/generate`.

## 1.3.4
* Reduce `non-secure` size.
* Add `async` callback type check.

## 1.3.3
* Fix `nanoid/async` performance regression.
* Fix old Node.js `not seeded` issue in synchronous version too.

## 1.3.2
* Fix random generator `not seeded` issue of old Node.js.

## 1.3.1
* Reduce library size.

## 1.3
* Add `nanoid/async/format` and `nanoid/async/generate`.
* Improve synchronous API performance.
* Reduce `url` size (by Daniil Poroshin).
* Improve React Native docs (by joelgetaction).

## 1.2.6
* Reduce library size (by rqrqrqrq).

## 1.2.5
* Fix Node.js 6.11.1 support (by Andrey Belym).

## 1.2.4
* Speed up Node.js secure generators (by Dmitriy Tsvettsikh).

## 1.2.3
* Fix JSDoc (by Hendry Sadrak).

## 1.2.2
* Fix distribution in `nanoid/non-secure` (by Eatall).

## 1.2.1
* Fix old Node.js support.

## 1.2
* Add `nanoid/async`.
* Fix `nanoid/non-secure` JSDoc.
* Add Chinese documentation (by Wenliang Dai).
* Speed up and reduce size of `nanoid/non-secure` (by Ori Livni).

## 1.1.1
* Improve performance and reduce size of non-secure ID generator.

## 1.1
* Add non-secure ID generator.
* Suggest to use non-secure ID generator for React Native developers.
* Reduce size.

## 1.0.7
* Fix documentation.

## 1.0.6
* Fix documentation.

## 1.0.5
* Reduce `nanoid/index` size (by Anton Khlynovskiy).

## 1.0.4
* Reduce npm package size.

## 1.0.3
* Reduce npm package size.

## 1.0.2
* Fix Web Workers support (by Zachary Golba).

## 1.0.1
* Reduce `nanoid/index` size (by Anton Khlynovskiy).

## 1.0
* Use 21 symbols by default (by David Klebanoff).

## 0.2.2
* Reduce `nanoid/generate` size (by Anton Khlynovskiy).
* Speed up Node.js random generator.

## 0.2.1
* Fix documentation (by Piper Chester).

## 0.2
* Add `size` argument to `nanoid()`.
* Improve performance by 50%.
* Reduce library size by 26% (by Vsevolod Rodionov and Oleg Mokhov).

## 0.1.1
* Reduce library size by 5%.

## 0.1
* Initial release.

ai/nanoid/blob/main/README.id-ID.md:

# Nano ID

<img src="https://ai.github.io/nanoid/logo.svg" align="right"
     alt="Logo Nano ID oleh Anton Lovchikov" width="180" height="94">

[English](./README.md) | [Русский](./README.ru.md) | [简体中文](./README.zh-CN.md) | **Bahasa Indonesia**

Sebuah generator ID yang unik dalam bentuk string yang ringan, aman, serta _URL-friendly_ untuk JavaScript.

> "Sebuah tingkat kesempurnaan yang luar biasa,
> yang mana tidak mungkin untuk tidak dihormati."

- **Ringan.** Hanya 116 bytes (diperkecil dan brotlied). Tidak ada ketergantungan (dependencies) apapun. [Size Limit](https://github.com/ai/size-limit) mengatur ukuran dari generator ini.
- **Aman.** Nano ID menggunakan RNG yang terdapat pada perangkat keras. Dapat digunakan dalam lingkungan seperti klaster.
- **ID yang pendek.** Nano ID menggunakan alfabet yang lebih banyak ketimbang UUID (`A-Za-z0-9_-`), karenanya ukuran ID menjadi berkurang dari 36 menjadi 21 simbol.
- **Portabel.** Nano ID telah dimigrasi untuk [20 bahasa pemrograman lainnya](#bahasa-pemrograman-lainnya).

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

Mendukung penjelajah (browser) modern, IE dengan Babel, Node.js, dan React Native.


  Made at Evil Martians, product consulting for developer tools.


Table of Contents

Perbandingan dengan UUID

Nano ID dapat dibandingkan dengan UUID v4 (yang berbasis acak / randomly generated). Nano ID dan UUID v4 memiliki jumlah bita yang mirip pada ID yang dihasilkan (126 bita pada NanoID dan 122 bita pada UUID), karenanya ia memiliki probabilitas collision (konflik / tabrakan) yang hampir serupa:

Agar timbul kemungkinan collison / duplikasi ID satu dalam satu miliar, perlu dihasilkan 103 triliun UUID v4.

Ada dua buah perbedaan antara Nano ID dan UUID v4:

  1. Nano ID menggunakan alfabet yang lebih lebar, karenanya jumlah bita acak dapat 'dikemas' dalam 21 simbol, bukan 36 simbol.
  2. Kode sumber Nano ID empat kali lebih kecil ketimbang uuid/v4: 130 bytes dibanding 423 bytes.

Benchmark

$ node ./test/benchmark.js
crypto.randomUUID          7,619,041 ops/sec
uuid v4                    7,436,626 ops/sec
@napi-rs/uuid              4,730,614 ops/sec
uid/secure                 4,729,185 ops/sec
@lukeed/uuid               4,015,673 ops/sec
nanoid                     3,693,964 ops/sec
customAlphabet             2,799,255 ops/sec
nanoid for browser           380,915 ops/sec
secure-random-string         362,316 ops/sec
uid-safe.sync                354,234 ops/sec
shortid                       38,808 ops/sec

Non-secure:
uid                       11,872,105 ops/sec
nanoid/non-secure          2,226,483 ops/sec
rndm                       2,308,044 ops/sec

Konfigurasi pengujian: Framework 13 7840U, Fedora 39, Node.js 21.6.

Keamanan

Lihat artikel yang informatif tentang teori angka acak: Nilai acak yang aman dalam Node.js (English).

  • Ketidakpastian. Sebagai ganti untuk penggunaan Math.random(), Nano ID menggunakan modul crypto yang ada di dalam Nodejs dan/atau Web Crypto API dalam penjelajah (browser). Modul-modul ini menggunakan generator acak berbasis perangkat keras yang tidak bisa diprediksi untuk mendapatkan nilai-nilai yang tidak pasti yang aman secara kriptografis.

  • Keseragaman. Pembagian dengan rumus random % alphabet adalah kesalahan yang seringkali dilakukan ketika merancang sebuah generator ID. Distribusi dari nilai-nilai tersebut tidak akan seimbang; dalam artian ada kesempatan untuk beberapa simbol untuk muncul dibandingkan dengan simbol yang lain. Ini memiliki dampak yang kurang baik, yakni mengurangi jumlah percobaan ketika seseorang mencoba untuk melakukan brute-force attacks. Nano ID menggunakan algoritma yang lebih baik dan sudah diuji untuk keseragamannya.

    Nano ID uniformity

  • Terdokumentasi secara baik. Seluruh algoritma Nano ID sudah terdokumentasi. Lihat komentar di kode sumber.

  • Kerentanan. Untuk melaporkan sebuah security vulnerability atau kerentanan, mohon menggunakan Tidelift Security Contact. Tidelift akan mengkoordinasikan pembetulan dan penyingkapan dari kerentanan tersebut.

Instalasi

npm install nanoid

Nano ID 5 hanya tersedia untuk proyek, pengujian, atau skrip ESM Node.js. Untuk CommonJS Anda memerlukan Nano ID 3.x (kami masih mendukungnya):

npm install nanoid@3

Apabila ingin 'coba-coba' terlebih dahulu, dapat digunakan Nano ID melalui CDN. Hal ini tidak direkomendasikan untuk digunakan pada lingkungan produksi karena performa pemuatan (loading) yang berkurang.

import { nanoid } from 'https://cdn.jsdelivr.net/npm/nanoid/nanoid.js'

API

Nano ID memiliki dua API: normal dan non-secure.

Bawaannya, Nano ID menggunakan simbol yang URL-friendly (A-Za-z0-9_-) dan mengembalikan ID dengan 21 karakter (untuk memiliki probabilitas collision / tabrakan yang mirip dengan UUID v4).

Blocking

Penggunaan Nano ID yang aman dan yang paling mudah.

Dalam kasus langka, fungsi ini dapat menghambat CPU untuk melakukan proses yang lain ketika dalam proses 'noise-collection' untuk generasi nilai acak (yang dilakukan pada perangkat keras).

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

Apabila ingin mengurangi ukuran ID (dan meningkatkan probabilitas collision), dapat dimasukkan size sebagai argumen dari fungsi nanoid().

nanoid(10) //=> "IRFa-VaY2b"

Jangan lupa memeriksa tingkat keamanan dari ukuran ID dalam situs ID collision probability calculator.

Dapat digunakan pula custom alphabet atau random generator yang lain.

Non-Secure

Konfigurasi bawaan Nano ID menggunakan random bytes generator yang berasal dari perangkat keras untuk keamanan dan probabilitas collision yang rendah. Apabila tidak terlalu memikirkan soal keamanan, dapat pula menggunakan non-secure generator yang lebih cepat.

import { nanoid } from 'nanoid/non-secure'
const id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqLJ"

Alfabet dan Ukuran (Custom)

customAlphabet digunakan untuk membuat Nano ID dengan alfabet dan ukuran ID yang sesuai dengan kebutuhan (dapat dikustomisasi).

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

Ketika menggunakan fungsi ini, jangan lupa untuk memeriksa keamanan alfabet dan ukuran ID dalam ID collision probability calculator. Untuk lebih banyak alfabet, dapat menggunakan nanoid-dictionary.

Alfabet harus terbentuk dari 256 simbol atau lebih kecil. Selain itu, keamanan algoritma generasi yang berada di dalam library ini tidak dijamin aman.

API non-secure yang dapat dikustomisasi dengan customAlphabet pun tersedia disini:

import { customAlphabet } from 'nanoid/non-secure'
const nanoid = customAlphabet('1234567890abcdef', 10)
user.id = nanoid()

Generasi Random Bytes (Custom)

customRandom digunakan untuk membuat Nano ID yang mengganti alfabet dan algoritma random bytes generator yang telah diimplementasikan pada versi bawaan (dalam artian menggunakan algoritma sendiri untuk mendapatkan random bytes).

Pada contoh berikut, digunakan seed-based generator:

import { customRandom } from 'nanoid'

const rng = seedrandom(seed)
const nanoid = customRandom('abcdef', 10, size => {
  return new Uint8Array(size).map(() => 256 * rng())
})

nanoid() //=> "fbaefaadeb"

Fungsi callback pada random harus menerima ukuran array dan mengembalikan sebuah array dengan angka acak.

Apabila ingin menggunakan alfabet bawaan NanoID pada fungsi customRandom, dapat menggunakan konstanta urlAlphabet seperti berikut:

const { customRandom, urlAlphabet } = require('nanoid')
const nanoid = customRandom(urlAlphabet, 10, random)

API asinkronus dan non-secure tidak tersedia untuk fungsi customRandom.

Penggunaan

React

Dalam React, tidak ada cara yang benar bila ingin menggunakan Nano ID untuk prop key, karena key tersebut harus konsisten dalam setiap proses render yang terjadi.

function Todos({ todos }) {
  return (
    <ul>
      {todos.map(todo => (
        /* JANGAN DILAKUKAN! */
        <li key={nanoid()}>{todo.text}</li>
      ))}
    </ul>
  )
}

Karena hal tersebut, disarankan untuk menggunakan ID yang stabil pada setiap objek yang di-render oleh React.

const todoItems = todos.map(todo => <li key={todo.id}>{todo.text}</li>)

Apabila tidak memiliki ID yang stabil pada setiap item yang di-render pada React, lebih baik menggunakan indeks sebuah array sebagai key ketimbang menggunakan fungsi nanoid(), seperti berikut:

const todoItems = todos.map((text, index) => (
  /* Tetap tidak direkomendasikan, tetapi lebih disarankan dari 'nanoid()'. Lakukan ini
    apabila setiap objek / item dalam list tidak ada ID yang stabil. */
  <li key={index}>{text}</li>
))

React Native

React Native tidak memiliki built-in random generator. Digunakan polyfill seperti berikut yang berjalan untuk React Native dan Expo yang bermula dari versi 39.x.

  1. Periksa dokumentasi react-native-get-random-values dan install di aplikasi.
  2. Import library tersebut sebelum Nano ID.
import 'react-native-get-random-values'
import { nanoid } from 'nanoid'

PouchDB dan CouchDB

Dalam PouchDB dan CouchDB, ID tidak bisa dimulai dengan underscore _. Sebuah prefix dibutuhkan untuk mencegah hal ini terjadi, karena Nano ID mungkin menggunakan _ sebagai karakter pertama dari ID yang dihasilkan.

ID bawaan dapat diubah dengan opsi berikut:

db.put({
  _id: 'id' + nanoid(),})

Web Workers

Web Workers tidak memiliki akses untuk secure random generator.

Keamanan sangat penting pada ID yang mana setiap ID harus memiliki sifat tidak bisa diprediksi, seperti pada contoh use-case generasi link pada "access by URL". Apabila tidak memerlukan ID yang tidak bisa diprediksi, tetapi ingin/harus menggunakan Web Workers, dapat digunakan NanoID dengan API non-secure.

import { nanoid } from 'nanoid/non-secure'
nanoid() //=> "Uakgb_J5m9g-0JDMbcJqLJ"

Perhatian: ID yang dihasilkan dari non-secure dapat lebih mudah tabrakan / memiliki probabilitas collision yang lebih besar.

CLI

Nano ID dapat didapatkan dengan cara menggunakan npx nanoid pada Terminal. Hanya diperlukan Node.js untuk ini, dan tidak perlu mengunduh dan menginstall Nano ID dalam sistem.

$ npx nanoid
npx: installed 1 in 0.63s
LZfXLFzPPR4NNrgjlWDxn

Bila ingin mengganti alfabet atau ukuran ID, dapat menggunakan nanoid-cli.

Bahasa Pemrograman Lainnya

Nano ID telah bermigrasi ke berbagai macam bahasa. Seluruh versi dapat digunakan untuk mendapatkan ID generator yang sama pada sisi klien dan sisi penyedia layanan (client-side dan server-side).

Untuk environment lainnya, CLI tersedia untuk melakukan generasi ID dari command line / Terminal.

Alat

  • ID Size Calculator menunjukkan probabilitas collision ketika melakukan konfigurasi alfabet dan ukuran.
  • nanoid-dictionary untuk menggunakan alfabet popular dalam fungsi customAlphabet
  • nanoid-good untuk meyakinkan bahwa ID yang di-generasi tidak memiliki kata-kata yang kurang baik (kasar, tidak sopan, dsb.).

ai/nanoid/blob/main/README.md:

```md
# Nano ID

<img src="https://ai.github.io/nanoid/logo.svg" align="right"
     alt="Nano ID logo by Anton Lovchikov" width="180" height="94">

**English** | [Русский](./README.ru.md) | [简体中文](./README.zh-CN.md) | [Bahasa Indonesia](./README.id-ID.md)

A tiny, secure, URL-friendly, unique string ID generator for JavaScript.

> “An amazing level of senseless perfectionism,
> which is simply impossible not to respect.”

* **Small.** 116 bytes (minified and brotlied). No dependencies.
  [Size Limit] controls the size.
* **Safe.** It uses hardware random generator. Can be used in clusters.
* **Short IDs.** It uses a larger alphabet than UUID (`A-Za-z0-9_-`).
  So ID size was reduced from 36 to 21 symbols.
* **Portable.** Nano ID was ported
  to over [20 programming languages](./README.md#other-programming-languages).

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

  Made at Evil Martians, product consulting for developer tools.


Table of Contents

Comparison with UUID

Nano ID is quite comparable to UUID v4 (random-based). It has a similar number of random bits in the ID (126 in Nano ID and 122 in UUID), so it has a similar collision probability:

For there to be a one in a billion chance of duplication, 103 trillion version 4 IDs must be generated.

There are two main differences between Nano ID and UUID v4:

  1. Nano ID uses a bigger alphabet, so a similar number of random bits are packed in just 21 symbols instead of 36.
  2. Nano ID code is 4 times smaller than uuid/v4 package: 130 bytes instead of 423.

Benchmark

$ node ./test/benchmark.js
crypto.randomUUID          7,619,041 ops/sec
uuid v4                    7,436,626 ops/sec
@napi-rs/uuid              4,730,614 ops/sec
uid/secure                 4,729,185 ops/sec
@lukeed/uuid               4,015,673 ops/sec
nanoid                     3,693,964 ops/sec
customAlphabet             2,799,255 ops/sec
nanoid for browser           380,915 ops/sec
secure-random-string         362,316 ops/sec
uid-safe.sync                354,234 ops/sec
shortid                       38,808 ops/sec

Non-secure:
uid                       11,872,105 ops/sec
nanoid/non-secure          2,226,483 ops/sec
rndm                       2,308,044 ops/sec

Test configuration: Framework 13 7840U, Fedora 39, Node.js 21.6.

Security

See a good article about random generators theory: Secure random values (in Node.js)

  • Unpredictability. Instead of using the unsafe Math.random(), Nano ID uses the crypto module in Node.js and the Web Crypto API in browsers. These modules use unpredictable hardware random generator.

  • Uniformity. random % alphabet is a popular mistake to make when coding an ID generator. The distribution will not be even; there will be a lower chance for some symbols to appear compared to others. So, it will reduce the number of tries when brute-forcing. Nano ID uses a better algorithm and is tested for uniformity.

    Nano ID uniformity

  • Well-documented: all Nano ID hacks are documented. See comments in the source.

  • Vulnerabilities: to report a security vulnerability, please use the Tidelift security contact. Tidelift will coordinate the fix and disclosure.

Install

npm install nanoid

Nano ID 5 works only with ESM projects, in tests or Node.js scripts. For CommonJS you need to use Node.js 22 with --experimental-require-module:

node --experimental-require-module app.js

Or you can use Nano ID 3.x (we still support it):

npm install nanoid@3

For quick hacks, you can load Nano ID from CDN. Though, it is not recommended to be used in production because of the lower loading performance.

import { nanoid } from 'https://cdn.jsdelivr.net/npm/nanoid/nanoid.js'

API

Nano ID has 2 APIs: normal and non-secure.

By default, Nano ID uses URL-friendly symbols (A-Za-z0-9_-) and returns an ID with 21 characters (to have a collision probability similar to UUID v4).

Blocking

The safe and easiest way to use Nano ID.

In rare cases could block CPU from other work while noise collection for hardware random generator.

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

If you want to reduce the ID size (and increase collisions probability), you can pass the size as an argument.

nanoid(10) //=> "IRFa-VaY2b"

Don’t forget to check the safety of your ID size in our ID collision probability calculator.

You can also use a custom alphabet or a random generator.

Non-Secure

By default, Nano ID uses hardware random bytes generation for security and low collision probability. If you are not so concerned with security, you can use it for environments without hardware random generators.

import { nanoid } from 'nanoid/non-secure'
const id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqLJ"

Custom Alphabet or Size

customAlphabet returns a function that allows you to create nanoid with your own alphabet and ID size.

import { customAlphabet } from 'nanoid'
const nanoid = customAlphabet('1234567890abcdef', 10)
model.id = nanoid() //=> "4f90d13a42"
import { customAlphabet } from 'nanoid/non-secure'
const nanoid = customAlphabet('1234567890abcdef', 10)
user.id = nanoid()

Check the safety of your custom alphabet and ID size in our ID collision probability calculator. For more alphabets, check out the options in nanoid-dictionary.

Alphabet must contain 256 symbols or less. Otherwise, the security of the internal generator algorithm is not guaranteed.

In addition to setting a default size, you can change the ID size when calling the function:

import { customAlphabet } from 'nanoid'
const nanoid = customAlphabet('1234567890abcdef', 10)
model.id = nanoid(5) //=> "f01a2"

Custom Random Bytes Generator

customRandom allows you to create a nanoid and replace alphabet and the default random bytes generator.

In this example, a seed-based generator is used:

import { customRandom } from 'nanoid'

const rng = seedrandom(seed)
const nanoid = customRandom('abcdef', 10, size => {
  return (new Uint8Array(size)).map(() => 256 * rng())
})

nanoid() //=> "fbaefaadeb"

random callback must accept the array size and return an array with random numbers.

If you want to use the same URL-friendly symbols with customRandom, you can get the default alphabet using the urlAlphabet.

const { customRandom, urlAlphabet } = require('nanoid')
const nanoid = customRandom(urlAlphabet, 10, random)

Note, that between Nano ID versions we may change random generator call sequence. If you are using seed-based generators, we do not guarantee the same result.

Usage

React

There’s no correct way to use Nano ID for React key prop since it should be consistent among renders.

function Todos({todos}) {
  return (
    <ul>
      {todos.map(todo => (
        <li key={nanoid()}> /* DON’T DO IT */
          {todo.text}
        </li>
      ))}
    </ul>
  )
}

You should rather try to reach for stable ID inside your list item.

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
)

In case you don’t have stable IDs you'd rather use index as key instead of nanoid():

const todoItems = todos.map((text, index) =>
  <li key={index}> /* Still not recommended but preferred over nanoid().
                      Only do this if items have no stable IDs. */
    {text}
  </li>
)

In case you just need random IDs to link elements like labels and input fields together, useId is recommended. That hook was added in React 18.

React Native

React Native does not have built-in random generator. The following polyfill works for plain React Native and Expo starting with 39.x.

  1. Check react-native-get-random-values docs and install it.
  2. Import it before Nano ID.
import 'react-native-get-random-values'
import { nanoid } from 'nanoid'

PouchDB and CouchDB

In PouchDB and CouchDB, IDs can’t start with an underscore _. A prefix is required to prevent this issue, as Nano ID might use a _ at the start of the ID by default.

Override the default ID with the following option:

db.put({
  _id: 'id' + nanoid(),})

Web Workers

Web Workers do not have access to a secure random generator.

Security is important in IDs when IDs should be unpredictable. For instance, in "access by URL" link generation. If you do not need unpredictable IDs, but you need to use Web Workers, you can use the non‑secure ID generator.

import { nanoid } from 'nanoid/non-secure'
nanoid() //=> "Uakgb_J5m9g-0JDMbcJqLJ"

Note: non-secure IDs are more prone to collision attacks.

CLI

You can get unique ID in terminal by calling npx nanoid. You need only Node.js in the system. You do not need Nano ID to be installed anywhere.

$ npx nanoid
npx: installed 1 in 0.63s
LZfXLFzPPR4NNrgjlWDxn

Size of generated ID can be specified with --size (or -s) option:

$ npx nanoid --size 10
L3til0JS4z

Custom alphabet can be specified with --alphabet (or -a) option (note that in this case --size is required):

$ npx nanoid --alphabet abc --size 15
bccbcabaabaccab

Other Programming Languages

Nano ID was ported to many languages. You can use these ports to have the same ID generator on the client and server side.

For other environments, CLI is available to generate IDs from a command line.

Tools


ai/nanoid/blob/main/README.ru.md:

```md
# Nano ID

<img src="https://ai.github.io/nanoid/logo.svg" align="right"
     alt="Логотип Nano ID от Антона Ловчикова" width="180" height="94">

[English](./README.md) | **Русский** | [简体中文](./README.zh-CN.md) | [Bahasa Indonesia](./README.id-ID.md)

Генератор уникальных ID для JavaScript — лёгкий, безопасный,
ID можно применять в URL.

> «Поразительный уровень бессмысленного перфекционизма,
> который просто невозможно не уважать»

- **Лёгкий.** 116 байт (после минификации и Brotli). Без зависимостей.
  [Size Limit] следит за размером.
- **Безопасный.** Использует аппаратный генератор случайных чисел.
  Можно использовать в кластерах машин.
- **Короткие ID.** Используется больший алфавит, чем у UUID (`A-Za-z0-9_-`).
  Поэтому длина ID уменьшена с 36 до 21 символа.
- **Работает везде.** Nano ID уже портировали
  на [20 языков программирования](#другие-языки-программирования).

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

Поддерживает современные браузеры, IE (с Babel), Node.js и React Native.


  Сделано в Злых марсианах, продуктовом консалтинге для инструментов разработки.


Оглавление

Сравнение с UUID

Nano ID похож на UUID v4 (случайный). У них сравнимое число битов случайности в ID (126 у Nano ID против 122 у UUID), поэтому они обладают похожей вероятностью возникновения коллизий (повторной генерации ранее выданных ID):

Чтобы вероятность повтора приблизилась к 1 на миллиард, нужно сгенерировать 103 триллиона ID.

Но между ними есть 2 важных отличия:

  1. Nano ID использует более широкий алфавит, и сравнимое количество битов случайности будут упакованы в более короткую строку (21 символ, против 36 у UUID).
  2. Код Nano ID в 4 раз меньше, чем у uuid/v4 — 130 байт против 423.

Сравнение производительности

$ node ./test/benchmark.js
crypto.randomUUID          7,619,041 ops/sec
uuid v4                    7,436,626 ops/sec
@napi-rs/uuid              4,730,614 ops/sec
uid/secure                 4,729,185 ops/sec
@lukeed/uuid               4,015,673 ops/sec
nanoid                     3,693,964 ops/sec
customAlphabet             2,799,255 ops/sec
nanoid for browser           380,915 ops/sec
secure-random-string         362,316 ops/sec
uid-safe.sync                354,234 ops/sec
shortid                       38,808 ops/sec

Non-secure:
uid                       11,872,105 ops/sec
nanoid/non-secure          2,226,483 ops/sec
rndm                       2,308,044 ops/sec

Среда сравнения: Framework 13 7840U, Fedora 39, Node.js 21.6.

Безопасность

См. также хорошую статью о теориях генераторов случайных чисел: Secure random values (in Node.js)

  • Непредсказуемость. Вместо предсказуемого Math.random(), Nano ID использует модуль crypto в Node.js и Web Crypto API в браузере. Эти модули дают доступ к аппаратному генератору случайных чисел.

  • Равномерность. Например, существует популярная ошибка random % alphabet, которую часто допускают при разработке генератора ID. Распределение вероятности для каждого символа может не быть одинаковым. Из-за неравномерности использования пространства алфавита, на перебор ID потребуется меньше времени, чем ожидается. Nano ID использует более совершенный алгоритм, а равномерность распределения символов покрыта тестами.

    Распределение Nano ID

  • Документация: все хитрости Nano ID хорошо документированы — смотрите комментарии в исходниках.

  • Уязвимости: если вы нашли уязвимость в Nano ID, свяжитесь с командой безопасности Tidelift. Они проконтролируют исправление и проинформируют пользователей.

Подключение

npm install nanoid

Nano ID 5 работает только с ESM-проектами, в тестах или скриптах для Node.js. Для CommonJS вам нужна Node.js 22 с флагом --experimental-require-module:

node --experimental-require-module app.js

Или возьмите Nano ID 3.x (мы его всё ещё поддерживаем):

npm install nanoid@3

Для быстрого прототипирования вы можете подключить Nano ID с CDN без установки. Не используйте этот способ на реальном сайте, так как он сильно бьёт по скорости загрузки сайта.

import { nanoid } from 'https://cdn.jsdelivr.net/npm/nanoid/nanoid.js'

API

Nano ID разделён на два модуля: стандартный и небезопасный.

По умолчанию используются символы, безопасные для URL (A-Za-z0-9_-). Длина ID по умолчанию — 21 символ (чтобы вероятность коллизий была соизмеримой с UUID v4).

Блокирующий

Безопасный и простой в использовании способ использования Nano ID.

Из-за особенностей работы генератора случайных чисел при использовании этого способа ЦПУ может иногда простаивать без работы.

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

Функция также принимает необязательный аргумент, задающий длину ID:

nanoid(10) //=> "IRFa-VaY2b"

При изменении размера, всегда проверяйте риски в нашем калькуляторе коллизий.

Небезопасный

По умолчанию, Nano ID использует аппаратный генератор случайных чисел для получения непредсказуемых ID и минимизации риска возникновения коллизий (повторной генерации ранее выданных ID). Но если вам не требуется устойчивость к подбору ID, то вы можете перейти на небезопасный генератор — это полезно там, где нет доступа к API аппаратного генератора случайных чисел.

import { nanoid } from 'nanoid/non-secure'
const id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqLJ"

Но учтите, что предсказуемость ID может быть использована для атаки на систему.

Смена алфавита или длины

Функция customAlphabet позволяет создать свою функцию nanoid с нужным вам алфавитом и длиной ID.

import { customAlphabet } from 'nanoid'
const nanoid = customAlphabet('1234567890abcdef', 10)
user.id = nanoid() //=> "4f90d13a42"
import { customAlphabet } from 'nanoid/non-secure'
const nanoid = customAlphabet('1234567890abcdef', 10)
user.id = nanoid()

Не забудьте проверить риски коллизии вашего алфавита и длины на нашем калькуляторе. nanoid-dictionary содержит много популярных примеров альтернативных алфавитов.

Алфавит должен содержать ≤256 символов. Иначе мы не сможем гарантировать непредсказуемость ID.

Длину ID можно менять не только в customAlphabet(), но и при вызове генератора, который она вернёт:

import { customAlphabet } from 'nanoid'
const nanoid = customAlphabet('1234567890abcdef', 10)
model.id = nanoid(5) //=> "f01a2"

Смена генератора случайных чисел

Функция customRandom позволяет создать свою функцию nanoid со своими генераторами случайных чисел, алфавитом и длинной ID.

Например, можно использовать генератор c seed для повторяемости тестов.

import { customRandom } from 'nanoid'

const rng = seedrandom(seed)
const nanoid = customRandom('abcdef', 10, size => {
  return new Uint8Array(size).map(() => 256 * rng())
})

nanoid() //=> "fbaefaadeb"

Функция в третьем аргументе customRandom должна принимать длину массива и возвращать нужный массив со случайными числами

Если вы хотите заменить только генератор случайных чисел, но оставить URL-совместимый алфавит, то стандартный алфавит доступен в экспорте urlAlphabet.

const { customRandom, urlAlphabet } = require('nanoid')
const nanoid = customRandom(urlAlphabet, 10, random)

Руководство

React

Не используйте Nano ID для генерации свойства key в JSX. При каждом рендере key будет разный, что плохо скажется на производительности.

function Todos({ todos }) {
  return (
    <ul>
      {todos.map(todo => (
        <li key={nanoid()}> /* НЕ ДЕЛАЙТЕ ТАК */
          {todo.text}
        </li>
      ))}
    </ul>
  )
}

Для связи <input> и <label> лучше использовать useId, который был добавлен в React 18.

React Native

React Native не имеет встроенного аппаратного генератора случайных чисел. Полифил ниже работает в чистом React Native и в Expo начиная с версии 39.

  1. Прочитайте документацию react-native-get-random-values и установите его.
  2. Импортируйте эту библиотеку до импорта Nano ID.
import 'react-native-get-random-values'
import { nanoid } from 'nanoid'

PouchDB и CouchDB

В PouchDB и CouchDB, ID не могут начинаться с _. Добавьте к ID префикс, так как иногда Nano ID может сгенерировать ID начинающийся с _.

Изменить стандартный ID можно через следующую опцию:

db.put({
  _id: 'id' + nanoid(),})

Веб-воркеры

Веб-воркеры не имеют доступа к аппаратному генератору случайных чисел.

Аппаратный генератор нужен, в том числе, для непредсказуемости ID. Например, когда доступ к секретному документу защищён ссылкой с уникальным ID.

Если вам не нужна непредсказуемость ID, то в Веб-воркере можно использовать небезопасный генератор ID.

import { nanoid } from 'nanoid/non-secure'
nanoid() //=> "Uakgb_J5m9g-0JDMbcJqLJ"

Терминал

Можно сгенерировать уникальный ID прямо из терминала, вызвав npx nanoid. Для этого в системе должна быть только Node.js. npx сама скачает Nano ID, если его нет в системе.

$ npx nanoid
npx: installed 1 in 0.63s
LZfXLFzPPR4NNrgjlWDxn

Длину генерируемых ID можно передать в аргументе --size (или -s):

$ npx nanoid --size 10
L3til0JS4z

Изменить алфавит можно при помощи аргумента --alphabet (ли -a) (в этом случае --size обязателен):

$ npx nanoid --alphabet abc --size 15
bccbcabaabaccab

Другие языки программирования

Nano ID был портирован на множество языков. Это полезно, чтобы сервер и клиент генерировали ID по одной схеме.

Для остальных сред можно использовать Nano ID для терминала.

Инструменты

  • Калькулятор длины ID поможет подобрать оптимальную длину ID, в зависимости от частоты выдачи ID и нужной надёжности системы.
  • nanoid-dictionary с популярными алфавитами для customAlphabet.
  • nanoid-good гарантирует, что в случайном ID не будет матерных слов.

ai/nanoid/blob/main/README.zh-CN.md:

```md
# Nano ID

<img src="https://ai.github.io/nanoid/logo.svg" align="right"
     alt="Nano ID logo by Anton Lovchikov" width="180" height="94">

[English](./README.md) | [Русский](./README.ru.md) | **简体中文** | [Bahasa Indonesia](./README.id-ID.md)

一个小巧、安全、URL友好、唯一的 JavaScript 字符串ID生成器。

> “一个惊人的无意义的完美主义水平,这简直让人无法不敬佩。”

* **小巧.** 116字节 (经过压缩和Brotli处理)。没有依赖。[Size Limit] 控制大小。
* **安全.** 它使用硬件随机生成器。可在集群中使用。
* **紧凑.** 它使用比 UUID(`A-Za-z0-9_-`)更大的字母表。因此,ID 大小从36个符号减少到21个符号。
* **可移植.** Nano ID 已被移植到 [20种编程语言](#其他编程语言)。

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

支持现代浏览器、IE [需使用 Babel]、Node.js 和 React Native。


  Made at Evil Martians, product consulting for developer tools.


目录

与 UUID 的比较

Nano ID 与 UUID v4 (基于随机数) 相当。 它们在 ID 中有相似数量的随机位 (Nano ID 为126,UUID 为122),因此它们的碰撞概率相似::

要想有十亿分之一的重复机会, 必须产生103万亿个版本4的ID.

Nano ID 和 UUID v4之间有两个主要区别:

  1. Nano ID 使用更大的字母表,所以类似数量的随机位 被包装在21个符号中,而不是36个。
  2. Nano ID 代码比 uuid/v4 包少 4倍: 130字节而不是423字节.

基准值

$ node ./test/benchmark.js
crypto.randomUUID          7,619,041 ops/sec
uuid v4                    7,436,626 ops/sec
@napi-rs/uuid              4,730,614 ops/sec
uid/secure                 4,729,185 ops/sec
@lukeed/uuid               4,015,673 ops/sec
nanoid                     3,693,964 ops/sec
customAlphabet             2,799,255 ops/sec
nanoid for browser           380,915 ops/sec
secure-random-string         362,316 ops/sec
uid-safe.sync                354,234 ops/sec
shortid                       38,808 ops/sec

Non-secure:
uid                       11,872,105 ops/sec
nanoid/non-secure          2,226,483 ops/sec
rndm                       2,308,044 ops/sec

测试配置: Framework 13 7840U, Fedora 39, Node.js 21.6.

安全性

请看一篇关于随机生成器理论的好文章: 安全的随机值 (在 Node.js 中)

  • 不可预测性. 不使用不安全的 Math.random(), Nano ID 使用 Node.js 的 crypto 模块和浏览器的 Web Crypto API。 这些模块使用不可预测的硬件随机生成器。

  • 统一性. random % alphabet 是编写ID生成器时常犯的一个错误。 这样做会导致分布不均匀; 一些符号出现的机会较其他符号低。因此,在暴力破解时,尝试的次数会减少。 Nano ID 使用了一种 更好的算法,并进行了一致性测试。

    Nano ID uniformity

  • 有据可查: 所有的 Nano ID 的行为都有记录。 见 源代码 中的注释。

  • 漏洞: 报告安全漏洞,请使用 安全联系人 Tidelift. Tidelift 将协调修复和披露。

安装

npm install nanoid

Nano ID 5 仅适用于 ESM 项目、测试或 Node.js 脚本。 对于 CommonJS,您需要 Nano ID 3.x(我们仍然支持它):

npm install nanoid@3

想要快速上手尝试,你可以从 CDN 加载 Nano ID。但是,它不建议 在生产中使用,因为它的加载性能较低。

import { nanoid } from 'https://cdn.jsdelivr.net/npm/nanoid/nanoid.js'

API

Nano ID 有2个 API:正常(阻塞),和不安全。

默认情况下,Nano ID 使用 URL 友好的符号(A-Za-z0-9_-)并返回一个 有21个字符(类似 UUID v4 的碰撞概率)的 ID。

阻塞

使用 Nano ID 最安全、最简单的方法

在极少数情况下,当收集硬件随机生成器的噪声时,可能会阻塞CPU,导致无法进行其他工作。

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

如果你想要减小 ID 大小(但是会增加碰撞概率), 可以将大小作为参数传递

nanoid(10) //=> "IRFa-VaY2b"

别忘了在我们的 ID 碰撞概率 计算器中检查你的 ID 大小的安全性。

您也可以使用 自定义字母表 或者是 自定义生成器.

不安全

默认情况下,Nano ID 使用硬件随机字节生成以提供安全性和较低的碰撞概率。如果您对安全性不太担心,您可以在没有硬件随机生成器的环境中使用它

import { nanoid } from 'nanoid/non-secure'
const id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqLJ"

自定义字母或大小

customAlphabet 返回一个函数,允许您使用自定义字母表和ID大小创建 nanoid

import { customAlphabet } from 'nanoid'
const nanoid = customAlphabet('1234567890abcdef', 10)
model.id = nanoid() //=> "4f90d13a42"
import { customAlphabet } from 'nanoid/non-secure'
const nanoid = customAlphabet('1234567890abcdef', 10)
user.id = nanoid()

在我们的 ID 碰撞概率 计算器中检查您的自定义字母表和 ID 大小的安全性。 查看 nanoid-dictionary 中的选项以获取更多字母表。

字母表必须包含256个或更少的符号。 否则,无法保证内部生成器算法的安全性。

除了设置默认大小外,您还可以在调用函数时更改ID大小:

import { customAlphabet } from 'nanoid'
const nanoid = customAlphabet('1234567890abcdef', 10)
model.id = nanoid(5) //=> "f01a2"

自定义随机字节生成器

customRandom 允许您创建一个 nanoid 并替换字母表 和默认的随机字节生成器。

在此示例中,使用基于种子的生成器:

import { customRandom } from 'nanoid'

const rng = seedrandom(seed)
const nanoid = customRandom('abcdef', 10, size => {
  return (new Uint8Array(size)).map(() => 256 * rng())
})

nanoid() //=> "fbaefaadeb"

random 回调必须接受数组大小并返回随机数的数组。

如果要使用与 customRandom 相同的URL友好符号, 您可以使用 urlAlphabet 获取默认字母表。

const { customRandom, urlAlphabet } = require('nanoid')
const nanoid = customRandom(urlAlphabet, 10, random)

请注意,在Nano ID的不同版本之间,我们可能会更改随机生成器的调用顺序。如果您正在使用基于种子的生成器,我们不能保证相同的结果。

用法

React

目前还没有将 nanoid 用于 React key prop 的正确方法 因为它在不同的渲染中应该是一致的。

function Todos({todos}) {
  return (
    <ul>
      {todos.map(todo => (
        <li key={nanoid()}> /* 不要这样做 */
          {todo.text}
        </li>
      ))}
    </ul>
  )
}

您应该尝试在列表项中找到稳定的 id。

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
)

如果您没有稳定的 ID,您最好使用索引作为 而不是 nanoid()

const todoItems = todos.map((text, index) =>
  <li key={index}> /* 仍然不推荐,但优于 nanoid()。
                      仅当项目没有稳定ID时才执行此操作。 */
    {text}
  </li>
)

如果您只需要随机 ID 来将元素(如标签和输入字段)链接在一起,建议使用 useId。该钩子在 React 18 中添加。

React Native

React Native 没有内置的随机生成器。以下polyfill适用于纯 React Native 和 Expo,从39.x版本开始生效。

  1. 检查 react-native-get-random-values 文档并安装它。
  2. 在 Nano ID 之前导入它。
import 'react-native-get-random-values'
import { nanoid } from 'nanoid'

PouchDB and CouchDB

在 PouchDB 和 CouchDB 中,ID 不能以下划线 _ 开头。 需要一个前缀来防止这个问题,因为 Nano ID 可能在默认情况下使用 _ 作为 ID 的开头。 在默认情况下,在 ID 的开头使用 _

用下面的选项覆盖默认的 ID。

db.put({
  _id: 'id' + nanoid(),})

Web Workers

Web Workers 无法访问安全的随机生成器.

当ID应该是不可预测的时候,安全性对ID很重要。 例如,在 "按 URL 访问"的链接生成中。 如果你不需要不可预测的 ID,但你需要使用 Web Workers。 你可以使用非安全的 ID 生成器。

import { nanoid } from 'nanoid/non-secure'
nanoid() //=> "Uakgb_J5m9g-0JDMbcJqLJ"

注意:非安全的ID更容易受到碰撞攻击。

CLI

你可以通过调用 npx nanoid 在终端获得唯一的 ID。你只需要 在系统中安装了 Node.js。你不需要把 Nano ID 安装在任何地方。

$ npx nanoid
npx: installed 1 in 0.63s
LZfXLFzPPR4NNrgjlWDxn

生成的ID的大小可以使用 --size(或 -s )选项指定:

$ npx nanoid --size 10
L3til0JS4z

可以使用 --alphabet(或 -a )选项指定自定义字母表(请注意,这种情况下需要 --size 选项)。

$ npx nanoid --alphabet abc --size 15
bccbcabaabaccab

其他编程语言

Nano ID 已被移植到许多语言。 你可以使用下面这些移植,获取在客户端和服务器端相同的ID生成器。

此外,CLI 还可用于从命令行生成 ID。

工具


ai/nanoid/blob/main/eslint.config.js:

```js
import loguxConfig from '@logux/eslint-config'

/** @type {import('eslint').Linter.FlatConfig[]} */
export default [
  { ignores: ['test/demo/build', 'nanoid.js', '**/errors.ts'] },
  ...loguxConfig,
  {
    rules: {
      'func-style': 'off',
      'n/no-unsupported-features/node-builtins': 'off',
      'yoda': 'off'
    }
  }
]

ai/nanoid/blob/main/index.browser.js:

// This file replaces `index.js` in bundlers like webpack or Rollup,
// according to `browser` config in `package.json`.

import { urlAlphabet as scopedUrlAlphabet } from './url-alphabet/index.js'

export { urlAlphabet } from './url-alphabet/index.js'

export let random = bytes => crypto.getRandomValues(new Uint8Array(bytes))

export let customRandom = (alphabet, defaultSize, getRandom) => {
  // First, a bitmask is necessary to generate the ID. The bitmask makes bytes
  // values closer to the alphabet size. The bitmask calculates the closest
  // `2^31 - 1` number, which exceeds the alphabet size.
  // For example, the bitmask for the alphabet size 30 is 31 (00011111).
  // `Math.clz32` is not used, because it is not available in browsers.
  let mask = (2 << (Math.log(alphabet.length - 1) / Math.LN2)) - 1
  // Though, the bitmask solution is not perfect since the bytes exceeding
  // the alphabet size are refused. Therefore, to reliably generate the ID,
  // the random bytes redundancy has to be satisfied.

  // Note: every hardware random generator call is performance expensive,
  // because the system call for entropy collection takes a lot of time.
  // So, to avoid additional system calls, extra bytes are requested in advance.

  // Next, a step determines how many random bytes to generate.
  // The number of random bytes gets decided upon the ID size, mask,
  // alphabet size, and magic number 1.6 (using 1.6 peaks at performance
  // according to benchmarks).

  // `-~f => Math.ceil(f)` if f is a float
  // `-~i => i + 1` if i is an integer
  let step = -~((1.6 * mask * defaultSize) / alphabet.length)

  return (size = defaultSize) => {
    let id = ''
    while (true) {
      let bytes = getRandom(step)
      // A compact alternative for `for (var i = 0; i < step; i++)`.
      let j = step
      while (j--) {
        // Adding `|| ''` refuses a random byte that exceeds the alphabet size.
        id += alphabet[bytes[j] & mask] || ''
        if (id.length === size) return id
      }
    }
  }
}

export let customAlphabet = (alphabet, size = 21) =>
  customRandom(alphabet, size, random)

export let nanoid = (size = 21) => {
  let id = ''
  let bytes = crypto.getRandomValues(new Uint8Array(size))
  while (size--) {
    // Using the bitwise AND operator to "cap" the value of
    // the random byte from 255 to 63, in that way we can make sure
    // that the value will be a valid index for the "chars" string.
    id += scopedUrlAlphabet[bytes[size] & 63]
  }
  return id
}

ai/nanoid/blob/main/index.d.ts:

/**
 * Generate secure URL-friendly unique ID.
 *
 * By default, the ID will have 21 symbols to have a collision probability
 * similar to UUID v4.
 *
 * ```js
 * import { nanoid } from 'nanoid'
 * model.id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqL"
 * ```
 *
 * @param size Size of the ID. The default size is 21.
 * @returns A random string.
 */
export function nanoid(size?: number): string

/**
 * Generate secure unique ID with custom alphabet.
 *
 * Alphabet must contain 256 symbols or less. Otherwise, the generator
 * will not be secure.
 *
 * @param alphabet Alphabet used to generate the ID.
 * @param defaultSize Size of the ID. The default size is 21.
 * @returns A random string generator.
 *
 * ```js
 * const { customAlphabet } = require('nanoid')
 * const nanoid = customAlphabet('0123456789абвгдеё', 5)
 * nanoid() //=> "8ё56а"
 * ```
 */
export function customAlphabet(
  alphabet: string,
  defaultSize?: number
): (size?: number) => string

/**
 * Generate unique ID with custom random generator and alphabet.
 *
 * Alphabet must contain 256 symbols or less. Otherwise, the generator
 * will not be secure.
 *
 * ```js
 * import { customRandom } from 'nanoid/format'
 *
 * const nanoid = customRandom('abcdef', 5, size => {
 *   const random = []
 *   for (let i = 0; i < size; i++) {
 *     random.push(randomByte())
 *   }
 *   return random
 * })
 *
 * nanoid() //=> "fbaef"
 * ```
 *
 * @param alphabet Alphabet used to generate a random string.
 * @param size Size of the random string.
 * @param random A random bytes generator.
 * @returns A random string generator.
 */
export function customRandom(
  alphabet: string,
  size: number,
  random: (bytes: number) => Uint8Array
): () => string

/**
 * URL safe symbols.
 *
 * ```js
 * import { urlAlphabet } from 'nanoid'
 * const nanoid = customAlphabet(urlAlphabet, 10)
 * nanoid() //=> "Uakgb_J5m9"
 * ```
 */
export const urlAlphabet: string

/**
 * Generate an array of random bytes collected from hardware noise.
 *
 * ```js
 * import { customRandom, random } from 'nanoid'
 * const nanoid = customRandom("abcdef", 5, random)
 * ```
 *
 * @param bytes Size of the array.
 * @returns An array of random bytes.
 */
export function random(bytes: number): Uint8Array

ai/nanoid/blob/main/index.js:

import { webcrypto as crypto } from 'node:crypto'

import { urlAlphabet as scopedUrlAlphabet } from './url-alphabet/index.js'

export { urlAlphabet } from './url-alphabet/index.js'

// It is best to make fewer, larger requests to the crypto module to
// avoid system call overhead. So, random numbers are generated in a
// pool. The pool is a Buffer that is larger than the initial random
// request size by this multiplier. The pool is enlarged if subsequent
// requests exceed the maximum buffer size.
const POOL_SIZE_MULTIPLIER = 128
let pool, poolOffset

function fillPool(bytes) {
  if (!pool || pool.length < bytes) {
    pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER)
    crypto.getRandomValues(pool)
    poolOffset = 0
  } else if (poolOffset + bytes > pool.length) {
    crypto.getRandomValues(pool)
    poolOffset = 0
  }
  poolOffset += bytes
}

export function random(bytes) {
  // `-=` convert `bytes` to number to prevent `valueOf` abusing
  fillPool((bytes -= 0))
  return pool.subarray(poolOffset - bytes, poolOffset)
}

export function customRandom(alphabet, defaultSize, getRandom) {
  // First, a bitmask is necessary to generate the ID. The bitmask makes bytes
  // values closer to the alphabet size. The bitmask calculates the closest
  // `2^31 - 1` number, which exceeds the alphabet size.
  // For example, the bitmask for the alphabet size 30 is 31 (00011111).
  let mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
  // Though, the bitmask solution is not perfect since the bytes exceeding
  // the alphabet size are refused. Therefore, to reliably generate the ID,
  // the random bytes redundancy has to be satisfied.

  // Note: every hardware random generator call is performance expensive,
  // because the system call for entropy collection takes a lot of time.
  // So, to avoid additional system calls, extra bytes are requested in advance.

  // Next, a step determines how many random bytes to generate.
  // The number of random bytes gets decided upon the ID size, mask,
  // alphabet size, and magic number 1.6 (using 1.6 peaks at performance
  // according to benchmarks).
  let step = Math.ceil((1.6 * mask * defaultSize) / alphabet.length)

  return (size = defaultSize) => {
    let id = ''
    while (true) {
      let bytes = getRandom(step)
      // A compact alternative for `for (let i = 0; i < step; i++)`.
      let i = step
      while (i--) {
        // Adding `|| ''` refuses a random byte that exceeds the alphabet size.
        id += alphabet[bytes[i] & mask] || ''
        if (id.length === size) return id
      }
    }
  }
}

export function customAlphabet(alphabet, size = 21) {
  return customRandom(alphabet, size, random)
}

export function nanoid(size = 21) {
  // `-=` convert `size` to number to prevent `valueOf` abusing
  fillPool((size -= 0))
  let id = ''
  // We are reading directly from the random pool to avoid creating new array
  for (let i = poolOffset - size; i < poolOffset; i++) {
    // It is incorrect to use bytes exceeding the alphabet size.
    // The following mask reduces the random byte in the 0-255 value
    // range to the 0-63 value range. Therefore, adding hacks, such
    // as empty string fallback or magic numbers, is unnecessary because
    // the bitmask trims bytes down to the alphabet size.
    id += scopedUrlAlphabet[pool[i] & 63]
  }
  return id
}

ai/nanoid/blob/main/nanoid.js:

let a="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";export let nanoid=(e=21)=>{let t="",r=crypto.getRandomValues(new Uint8Array(e));for(let n=0;n<e;n++)t+=a[63&r[n]];return t};

ai/nanoid/blob/main/non-secure/index.d.ts:

/**
 * Generate URL-friendly unique ID. This method uses the non-secure
 * predictable random generator with bigger collision probability.
 *
 * ```js
 * import { nanoid } from 'nanoid/non-secure'
 * model.id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqL"
 * ```
 *
 * @param size Size of the ID. The default size is 21.
 * @returns A random string.
 */
export function nanoid(size?: number): string

/**
 * Generate a unique ID based on a custom alphabet.
 * This method uses the non-secure predictable random generator
 * with bigger collision probability.
 *
 * @param alphabet Alphabet used to generate the ID.
 * @param defaultSize Size of the ID. The default size is 21.
 * @returns A random string generator.
 *
 * ```js
 * import { customAlphabet } from 'nanoid/non-secure'
 * const nanoid = customAlphabet('0123456789абвгдеё', 5)
 * model.id = //=> "8ё56а"
 * ```
 */
export function customAlphabet(
  alphabet: string,
  defaultSize?: number
): (size?: number) => string

ai/nanoid/blob/main/non-secure/index.js:

// This alphabet uses `A-Za-z0-9_-` symbols.
// The order of characters is optimized for better gzip and brotli compression.
// References to the same file (works both for gzip and brotli):
// `'use`, `andom`, and `rict'`
// References to the brotli default dictionary:
// `-26T`, `1983`, `40px`, `75px`, `bush`, `jack`, `mind`, `very`, and `wolf`
let urlAlphabet =
  'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'

export let customAlphabet = (alphabet, defaultSize = 21) => {
  return (size = defaultSize) => {
    let id = ''
    // A compact alternative for `for (var i = 0; i < step; i++)`.
    let i = size
    while (i--) {
      // `| 0` is more compact and faster than `Math.floor()`.
      id += alphabet[(Math.random() * alphabet.length) | 0]
    }
    return id
  }
}

export let nanoid = (size = 21) => {
  let id = ''
  // A compact alternative for `for (var i = 0; i < step; i++)`.
  let i = size
  while (i--) {
    // `| 0` is more compact and faster than `Math.floor()`.
    id += urlAlphabet[(Math.random() * 64) | 0]
  }
  return id
}

ai/nanoid/blob/main/package.json:

{
  "name": "nanoid",
  "version": "5.0.7",
  "description": "A tiny (116 bytes), secure URL-friendly unique string ID generator",
  "keywords": [
    "uuid",
    "random",
    "id",
    "url"
  ],
  "scripts": {
    "clean": "rm -rf coverage",
    "start": "vite test/demo/ --open",
    "test:coverage": "c8 bnt",
    "test:lint": "eslint .",
    "test:size": "pnpm clean && size-limit",
    "test": "pnpm run /^test:/"
  },
  "type": "module",
  "engines": {
    "node": "^18 || >=20"
  },
  "funding": [
    {
      "type": "github",
      "url": "https://github.com/sponsors/ai"
    }
  ],
  "author": "Andrey Sitnik <[email protected]>",
  "license": "MIT",
  "repository": "ai/nanoid",
  "exports": {
    ".": {
      "browser": "./index.browser.js",
      "default": "./index.js"
    },
    "./non-secure": "./non-secure/index.js",
    "./package.json": "./package.json"
  },
  "browser": {
    "./index.js": "./index.browser.js"
  },
  "react-native": {
    "./index.js": "./index.browser.js"
  },
  "bin": "./bin/nanoid.js",
  "sideEffects": false,
  "types": "./index.d.ts",
  "devDependencies": {
    "@babel/core": "^7.24.7",
    "@logux/eslint-config": "^53.2.0",
    "@lukeed/uuid": "^2.0.1",
    "@napi-rs/uuid": "^0.2.2",
    "@originjs/vite-plugin-commonjs": "^1.0.3",
    "@size-limit/file": "^11.1.4",
    "@size-limit/webpack": "^11.1.4",
    "@types/node": "^20.14.8",
    "better-node-test": "^0.5.1",
    "c8": "^10.1.2",
    "clean-publish": "^5.0.0",
    "eslint": "^9.5.0",
    "rndm": "^1.2.0",
    "secure-random-string": "^1.1.3",
    "shortid": "^2.2.16",
    "size-limit": "^11.1.4",
    "terser": "^5.31.1",
    "tinybench": "^2.8.0",
    "uid": "^2.0.2",
    "uid-safe": "^2.1.5",
    "uuid": "^10.0.0",
    "vite": "^5.3.1"
  },
  "size-limit": [
    {
      "name": "nanoid",
      "import": "{ nanoid }",
      "limit": "116 B"
    },
    {
      "name": "customAlphabet",
      "import": "{ customAlphabet }",
      "limit": "165 B"
    },
    {
      "name": "urlAlphabet",
      "import": "{ urlAlphabet }",
      "limit": "47 B"
    },
    {
      "name": "non-secure nanoid",
      "import": "{ nanoid }",
      "path": "non-secure/index.js",
      "limit": "88 B"
    },
    {
      "name": "non-secure customAlphabet",
      "import": "{ customAlphabet }",
      "path": "non-secure/index.js",
      "limit": "53 B"
    }
  ],
  "prettier": {
    "arrowParens": "avoid",
    "jsxSingleQuote": false,
    "quoteProps": "consistent",
    "semi": false,
    "singleQuote": true,
    "trailingComma": "none"
  },
  "clean-publish": {
    "cleanDocs": true,
    "cleanComments": true
  },
  "c8": {
    "exclude": [
      "bin/nanoid.js",
      "**/*.test.*",
      "test/*"
    ],
    "lines": 100,
    "check-coverage": true,
    "reporter": [
      "text",
      "lcov"
    ],
    "skip-full": true,
    "clean": true
  }
}

ai/nanoid/blob/main/url-alphabet/index.js:

// This alphabet uses `A-Za-z0-9_-` symbols.
// The order of characters is optimized for better gzip and brotli compression.
// Same as in non-secure/index.js
export const urlAlphabet =
  'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
{
"url": "https://github.com/ai/nanoid",
"type": "github",
"title": "ai/nanoid",
"picture": "https://avatars.githubusercontent.com/u/19343?v=4",
"description": "JavaScript / 1.8K lines of code.\nA tiny (124 bytes), secure, URL-friendly, unique string ID generator for JavaScript"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment