JavaScript & TypeScript UUIDs
On this page
For random UUIDs (v4), modern JavaScript has it built-in: crypto.randomUUID(). No dependency, no setup. For other versions or for older runtimes, use the uuid npm package.
v4 in one line
crypto.randomUUID()
// "0e6f1b8c-2c33-4f1f-9c0b-2a3d4e5f6a7b"
crypto is the Web Crypto API — globally available in:
| Runtime | Available since |
|---|---|
| Modern browsers (Chrome, Firefox, Safari, Edge) | 2022 |
| Node.js (global) | 19.0 |
Node.js (require("crypto").randomUUID()) | 14.17 |
| Bun | All versions |
| Deno | All versions |
| Cloudflare Workers | All versions |
For Node 14–18, replace crypto.randomUUID() with require("crypto").randomUUID() or pull in the uuid package.
Other versions: the uuid package
npm install uuid
# or
pnpm add uuid
# or
bun add uuid
import { v1, v3, v4, v5, v7, NIL, validate, version } from "uuid";
v1(); // time-based
v4(); // random (same as crypto.randomUUID)
v7(); // time-ordered Unix-ms
v3("example.com", "6ba7b810-9d1d-11d1-80b4-00c04fd430c8"); // MD5 namespace
v5("https://x.io", v5.URL); // SHA-1 namespace, built-in URL ns
NIL; // "00000000-0000-0000-0000-000000000000"
validate("12fbc73a-9e9d-44b1-9e6d-1d3e7c8a0f31"); // true
version("12fbc73a-9e9d-44b1-9e6d-1d3e7c8a0f31"); // 4
The package is ~3 KB minified+gzipped and tree-shakes — if you only import v7, you only pay for v7.
TypeScript: typing UUIDs
The uuid package returns string. That’s correct but loose — every string is assignable to your UUID variables, including obvious mistakes:
function getUser(id: string) { /* ... */ }
getUser("hello"); // ❌ no error, but obviously wrong
For stricter typing, use a branded type:
type UUID = string & { readonly __brand: "UUID" };
function asUUID(s: string): UUID {
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s)) {
throw new Error(`Not a UUID: ${s}`);
}
return s as UUID;
}
function getUser(id: UUID) { /* ... */ }
getUser("hello"); // ❌ type error — string isn't UUID
getUser(asUUID(input)); // ✅
The runtime cost is just the regex check at the boundary; everywhere else it’s a free string at runtime.
If you’d rather keep things simple, the type-fest package exports a built-in UUID template literal type:
import type { UUID } from "type-fest";
const id: UUID = "0e6f1b8c-2c33-4f1f-9c0b-2a3d4e5f6a7b";
const bad: UUID = "hello"; // ❌ type error
This gives compile-time format checking with no runtime cost, but only catches literal-string mistakes — not strings whose value is computed.
React / Next.js
For client component IDs, prefer useId():
import { useId } from "react";
const id = useId(); // ":r1:" — stable, SSR-safe, not a UUID
useId() is for DOM id attributes; UUIDs are for entity identity (database rows, etc.). Don’t use UUIDs for <input id> — they hurt SSR hydration when generated on the client.
For server-side entity creation in Next.js Server Actions or Route Handlers:
"use server";
import { randomUUID } from "node:crypto";
export async function createOrder(data: FormData) {
const id = randomUUID();
await db.insert(orders).values({ id, ...data });
return id;
}
Browser-only (no bundler) v7
If you need v7 in vanilla JS without npm, here’s a 12-line implementation:
function uuidv7() {
const ts = Date.now();
const rand = crypto.getRandomValues(new Uint8Array(10));
const hex = [
ts.toString(16).padStart(12, "0"),
"7" + Array.from(rand.slice(0, 1)).map(b => b.toString(16).padStart(2, "0")).join("").slice(1),
(((rand[2] & 0x3f) | 0x80).toString(16).padStart(2, "0")) +
Array.from(rand.slice(3, 4)).map(b => b.toString(16).padStart(2, "0")).join(""),
Array.from(rand.slice(4)).map(b => b.toString(16).padStart(2, "0")).join(""),
];
return `${hex[0].slice(0,8)}-${hex[0].slice(8)}-${hex[1]}-${hex[2]}-${hex[3]}`;
}
For production code, use the npm package — it handles edge cases (clock rewind, monotonic counters within the same millisecond) that the snippet above doesn’t.
Validation
import { validate, version } from "uuid";
const valid = validate("12fbc73a-9e9d-44b1-9e6d-1d3e7c8a0f31"); // true
const ver = version("12fbc73a-9e9d-44b1-9e6d-1d3e7c8a0f31"); // 4
For a regex-only check (no dependency):
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
const isUuid = (s) => UUID_RE.test(s);
Common pitfalls
Math.random()is not random enough. Don’t write() => "xxxx".replace(/x/g, () => Math.floor(Math.random() * 16).toString(16)). Usecrypto.randomUUID().- Don’t
JSON.stringifyaUUIDbrand wrapper. Branded types are erased at runtime — they serialize as plain strings. - Bundle size. If you only need v4, use
crypto.randomUUID()and skip the npm package entirely. crypto.randomUUID()requires HTTPS in browsers. It’s part of the secure context API. Onhttp://you’ll getundefined. (localhostis treated as secure.)
Cheat sheet
| Goal | Code |
|---|---|
| Random UUID (browsers, Node 19+, Bun) | crypto.randomUUID() |
| Random UUID (older Node) | require("crypto").randomUUID() |
| Time-ordered (DB key) | import { v7 } from "uuid" |
| Deterministic | import { v5 } from "uuid" |
| Validate | import { validate } from "uuid" |
| TypeScript brand | type UUID = string & { __brand: "UUID" } |
Try the tools
- UUID generator — instant v4, copy with one click
- UUID v7 generator — modern primary keys
- UUID validator — paste any UUID, decode it