はじめに
「データベースの主キーやReactのkey属性に使うユニークなID、どうやって生成していますか?」
多くの方が真っ先に思い浮かべるのはUUIDでしょう。しかし、110ec58a-a0f2-4ac4-8393-c866d813b8d1 のような36文字のIDは、URLに含めると長くて不格好ですし、ハイフンが視認性を下げます。かといって Math.random() で自作すると、衝突(重複)のリスクや予測可能性というセキュリティ上の問題を抱えることになります。
そんな悩みを一気に解決してくれるのが「Nano ID」です。わずか118バイト(minified + brotli圧縮時)という驚異的な軽さで、UUIDと同等の安全性を持つ21文字のURLフレンドリーなIDを生成できます。この記事では、Nano IDの基本から実践的な使い方まで、コピペで動くコード例とともに解説します。
とはいえ、読むより触った方が早いと思います。nanoid() を実際にブラウザ上で呼び出して、桁数を変えながらIDが生成される様子を見られるサンプルを用意しました。先に挙動を見たい方はこちらからどうぞ。
Nano IDとは
Nano IDは、JavaScript向けの小さくて安全なユニークID生成ライブラリです。ロシアのAndrey Sitnik氏(PostCSSやAutoprefixerの作者としても有名です)によって開発され、GitHubで25k以上のスターを獲得しています。
npmでのダウンロード数は週間数千万回にのぼり、事実上の標準ID生成ライブラリとして幅広いプロジェクトで採用されています。MITライセンスのオープンソースで、2026年7月にはv6系がリリースされるなど、現在も活発に開発が続いています。
主な特徴
- 超軽量 - minified + brotli圧縮でわずか118バイト。依存パッケージはゼロなので、バンドルサイズをほとんど増やしません
- 高速かつ安全 - ハードウェア乱数生成器(Web Crypto API)を使用し、暗号学的に強度のあるIDを生成します。それでいてネイティブの
crypto.randomUUID()より高速です - 短くてURLフレンドリー - デフォルトのアルファベットは
A-Za-z0-9_-の64文字。UUIDの36文字に対して21文字と短く、そのままURLに埋め込めます - 衝突確率はUUIDv4と同等 - 記号を増やしてIDを短くしただけで、乱数の情報量は確保されています。10億分の1の確率で重複が起きるには、103兆個のIDを生成する必要があります
- 多言語対応 - 本家のJavaScript版以外にも、Python、Go、Rust、Javaなど20以上の言語に移植されています
インストール
npmやyarn、pnpmでインストールできます。
# npm
npm install nanoid
# yarn
yarn add nanoid
# pnpm
pnpm add nanoid
DenoやBunではJSR経由でもインストールできます。
npx jsr add @sitnik/nanoid
ビルドツールを使わない場合は、CDNから直接インポートすることも可能です。
import { nanoid } from 'https://cdn.jsdelivr.net/npm/nanoid/nanoid.js'
なお、Nano IDはESMパッケージですが、Node.js 22.12以降であればCommonJSの require() からも読み込めます。
Nano IDのサンプルを動かす
さっそくNano IDを動かしてみましょう。以下のサンプルは、ボタンを押すたびに nanoid() を呼び出して新しいIDを画面に追加していきます。上部の入力欄で桁数を変更すると、nanoid(length) のように引数へ反映され、生成されるIDの長さが変わります。
要点だけを抜き出すと、次のようなコードでIDを生成しています。
import { nanoid } from 'nanoid'
// デフォルト(21文字)でIDを生成する
const id = nanoid()
// 引数に数値を渡すと、その桁数でIDを生成する
const shortId = nanoid(10)
console.log(id) // => "V1StGXR8_Z5jdHi6B-myT"
console.log(shortId) // => "IRFa-VaY2b"
実際に触れるサンプルが以下です。桁数を1〜30の範囲で変えながら、「IDを生成」ボタンを押してみてください。
ボタンを押すたびに A-Za-z0-9_- の64文字から選ばれた新しいIDが1行ずつ追加されていきます。桁数を1桁や2桁のように極端に短くすると、何度か押すうちに同じIDが出てくる(衝突する)様子も体験できるはずです。逆に21文字のデフォルトのままでは、ブラウザを何度リロードしてもまず重複は起きません。次の章では、この nanoid() の使い方をもう少し詳しく見ていきます。
基本的な使い方
IDを生成する
使い方は驚くほどシンプルです。nanoid() を呼び出すだけで、21文字のユニークIDが得られます。
import { nanoid } from 'nanoid'
const id = nanoid()
console.log(id) // => "V1StGXR8_Z5jdHi6B-myT"
たったこれだけです。設定も初期化も不要で、インポートしてすぐに使えます。
長さを変更する
引数に数値を渡すと、IDの長さを変更できます。
import { nanoid } from 'nanoid'
const shortId = nanoid(10)
console.log(shortId) // => "IRFa-VaY2b"
ただし、短くするほど衝突確率は上がります。「何文字なら安全か」は公式のNano ID collision calculatorで計算できるので、生成頻度に応じて適切な長さを選びましょう。
カスタムアルファベットを使う
「数字だけのIDが欲しい」「紛らわしい文字(0 と O など)を除外したい」という場合は、customAlphabet を使います。
import { customAlphabet } from 'nanoid'
// 16進数文字のみ・10文字のID生成器を作る
const hexId = customAlphabet('1234567890abcdef', 10)
console.log(hexId()) // => "4f90d13a42"
第一引数に使いたい文字セット、第二引数にデフォルトの長さを指定すると、専用のID生成関数が返ってきます。
セキュリティが不要な場面では非セキュア版を
乱数の予測不可能性が重要でない用途(Reactのkey属性など)では、さらに高速な非セキュア版も選べます。
import { nanoid } from 'nanoid/non-secure'
const id = nanoid()
暗号学的な強度はないため、セッションIDやトークンなど推測されると困る用途には絶対に使わないでください。
実践的なユースケース
1. データベースの主キーとして使う
UUIDの代わりにNano IDを主キーに使うと、URLがすっきりします。以下はPrismaでの例です。
// prisma/schema.prisma では id を String 型で定義しておきます
import { PrismaClient } from '@prisma/client'
import { nanoid } from 'nanoid'
const prisma = new PrismaClient()
async function createArticle(title: string, body: string) {
return prisma.article.create({
data: {
id: nanoid(), // => "hJ8f2K_x9QzR3mNpL5vAw"
title,
body,
},
})
}
生成されたIDはそのまま https://example.com/articles/hJ8f2K_x9QzR3mNpL5vAw のようにURLへ埋め込めます。UUIDと違ってエンコードも不要です。
2. 短縮URLサービスの短いキーを作る
短縮URLのように「短さ」が命のケースでは、customAlphabet で読み間違えやすい文字を除外しつつ、長さを絞ります。
import { customAlphabet } from 'nanoid'
// 0/O、1/l/I など紛らわしい文字を除外した8文字のキー
const generateSlug = customAlphabet(
'23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ',
8
)
const slug = generateSlug()
console.log(`https://example.com/s/${slug}`) // => "https://example.com/s/xK7mP2vB"
ユーザーが口頭で伝えたり手入力したりする可能性のあるIDでは、この「紛らわしい文字の除外」が地味に効いてきます。
実際に customAlphabet でどんな文字セットが生成されるかを試せるサンプルが以下です。テキスト欄で使いたい文字(アルファベット・数字・記号など)を書き換え、桁数を指定してボタンを押すと、その場で customAlphabet から作った生成器がキーを返してくれます。
文字セットの入力欄から 0 や O、1 や l を消してみると、生成されるキーからもそれらの文字が消えることが確認できます。逆に文字セットを 0123456789 だけに絞れば、数字だけの短縮URLキーが得られます。customAlphabet の第一引数(文字セット)と第二引数(桁数)を変えるだけで、用途に合わせたID生成器を自由に作れるのがポイントです。
3. Reactでフォーム項目を動的に管理する
動的に増減するリスト項目のkeyには、配列のインデックスではなく安定したIDを使うのがReactのベストプラクティスです。
import { useState } from 'react'
import { nanoid } from 'nanoid'
type TodoItem = {
id: string
text: string
}
export function TodoList() {
const [items, setItems] = useState<TodoItem[]>([])
const addItem = (text: string) => {
setItems((prev) => [...prev, { id: nanoid(), text }])
}
return (
<div>
<button onClick={() => addItem('新しいタスク')}>追加</button>
<ul>
{items.map((item) => (
<li key={item.id}>{item.text}</li>
))}
</ul>
</div>
)
}
インデックスをkeyにすると項目の削除や並べ替えで表示が崩れることがありますが、Nano IDなら項目ごとに不変のIDが割り当てられるため安心です。
このReactコンポーネントをブラウザ上で実際に動かせるサンプルが以下です。「追加」を押すたびに nanoid() で生成したIDがkey属性に使われ、リストに項目が増えていきます。「先頭を削除」でインデックスがずれても、他の項目のkey(Nano IDそのもの)は変わらないことが確認できます。
key={item.id} の部分を試しに key={index}(配列のインデックス)に書き換えると、先頭を削除したときに他の項目のkeyも1つずつズレてしまい、Reactが不要な再レンダリングをしてしまうことが分かります。Nano IDのように項目ごとに不変のIDを割り当てておくことで、この問題を避けられます。
4. アップロードファイルの一意なファイル名を作る
ファイル名の衝突を避けたいときにも便利です。
import { nanoid } from 'nanoid'
import path from 'node:path'
function buildUploadFileName(originalName: string) {
const ext = path.extname(originalName) // 例: ".png"
return `${Date.now()}-${nanoid(12)}${ext}`
}
console.log(buildUploadFileName('photo.png'))
// => "1752384000000-mJ8xK2_p9QzR.png"
タイムスタンプと組み合わせれば、時系列ソートのしやすさと一意性を両立できます。
5. 生成したIDの一意性を目で確認する
「本当に衝突しないの?」という疑問は、実際に大量生成して確かめるのが一番です。以下は nanoid() を指定した個数だけ繰り返し呼び出し、Set を使って重複がないかをチェックするデモです。
import { nanoid } from 'nanoid'
const count = 10000
const ids = Array.from({ length: count }, () => nanoid())
// Setに入れて重複を除去し、件数を比較する
const uniqueCount = new Set(ids).size
console.log(`生成数: ${count} / ユニーク数: ${uniqueCount}`)
実際に個数を変えながら試せるサンプルが以下です。生成件数を入力して「生成して重複チェック」を押すと、nanoid() を件数分呼び出した配列を作り、Set のサイズと配列の長さを比較して重複件数を表示します。
10万件生成しても重複件数は基本的に0のままです。生成件数を桁違いに増やしていくと、Nano IDが21文字という短さでもUUIDと同等の衝突確率を持っていることを体感できます。逆に nanoid() を nanoid(4) のように短い桁数に変えて実行すると、数千件レベルでもあっさり重複が発生するはずです。
UUIDとの使い分け
「結局、UUIDとどちらを使えばいいの?」という疑問にお答えすると、判断基準は次のとおりです。
- Nano IDが向いているケース - URLに含めるID、フロントエンドでの生成、バンドルサイズを抑えたいSPA、文字数を柔軟に調整したい場合
- UUIDが向いているケース - RFC 9562準拠が求められるシステム間連携、データベースのネイティブUUID型(PostgreSQLの
uuid型など)をそのまま活かしたい場合
安全性(衝突確率・乱数品質)はどちらも同等なので、「規格への準拠が必要かどうか」と「短さが嬉しいかどうか」で選ぶとよいでしょう。
まとめ
今回は軽量ユニークID生成ライブラリ「Nano ID」を紹介しました。
- わずか118バイト・依存ゼロの超軽量ライブラリで、
nanoid()の1行でUUIDと同等の安全性を持つIDを生成できます - デフォルトで21文字・URLフレンドリーな文字セットなので、そのままURLやファイル名に使えます
customAlphabetを使えば、文字セットと長さを用途に合わせて自由に調整できます- データベースの主キー、短縮URL、Reactのkey属性、ファイル名生成など、活躍の場は多岐にわたります
「IDの生成方法なんてどれも同じ」と思っていた方こそ、一度Nano IDを触ってみてください。導入コストはほぼゼロなのに、URLの見た目やバンドルサイズという日々の開発体験が確実に良くなります。まずは手元のプロジェクトで npm install nanoid から始めてみましょう。