cheat sheet

uuid

Package-level reference for uuid on npm — v4 random, v5 namespace, v7 sortable, ESM-only since v9, and namespace pattern.

uuid

What it is

uuid is the canonical npm package for generating RFC 4122 (and now RFC 9562) UUIDs. It implements every version that's been standardized: v1 (timestamp + MAC), v3 (namespace + MD5), v4 (random — the most common), v5 (namespace + SHA-1), v6 (reordered v1 — sortable), v7 (Unix-timestamp + random — sortable, new in 2024-2025), and v8 (custom-domain).

It's the de-facto choice over hand-rolled crypto.randomUUID() for two reasons: (1) namespace-based deterministic UUIDs (v3, v5) aren't possible from the built-in API, and (2) v7 (sortable, monotonic, better B-tree locality) is increasingly the recommended choice for new databases — Node's built-in crypto.randomUUID() only emits v4.

Install

bash
# npm / pnpm / yarn / bun
npm install uuid
pnpm add uuid
yarn add uuid
bun add uuid

Output: runtime dep. Pure JS; ~1 KB gzipped after tree-shaking individual versions.

bash
# TypeScript declarations are now bundled in v9+ — no separate @types/uuid needed
# (still install for legacy v8 codebases)
npm install --save-dev @types/uuid    # only for v8 and below

Output: TS support — bundled since v9.

bash
# CLI tool — generate a UUID from the shell
npx uuid              # v4 by default
npx uuid v7           # v7 — sortable
npx uuid v5 ns:URL https://example.com

Output: quick interactive use. The uuid binary is installed by the package and works under npx.

Versioning & Node support

  • Current major line is 11.x (stable; cleanups, additional v7 hardening, full RFC 9562 compliance). The 10.x line introduced the new v7 monotonic counter. The 9.x line is the ESM-only transition.
  • v9 was a breaking change: ESM-only, drops CJS require("uuid"). Many codebases stuck on v8 because of CJS interop.
  • Pure JS; runs on any modern runtime — Node 14+ (for v11, prefers Node 18+ for crypto.webcrypto), Bun, Deno, Cloudflare Workers, browsers.
  • ESM only since v9. No require("uuid"); you must use import { v4 } from "uuid". CJS interop via dynamic import() works.
  • Always a runtime dependency — you call it at runtime to produce IDs.
  • Types are bundled in v9+.

Package metadata

  • Maintainer: Christoph Tavan (@ctavan) + the uuidjs org
  • Project home: github.com/uuidjs/uuid
  • Docs: github.com/uuidjs/uuid#readme
  • npm: npmjs.com/package/uuid
  • License: MIT
  • First released: 2010 (as node-uuid); renamed to uuid in 2017
  • Downloads: ~100 million per week — top-10 package on npm

Peer dependencies & extras

Zero runtime dependencies. Internally uses the platform crypto (WebCrypto in browsers, node:crypto in Node, all bundlers patch this correctly).

PackagePurpose
@types/uuidTS declarations (only needed for v8 and below — v9+ bundles types)
short-uuidEncodes UUIDs into shorter URL-safe representations (base57, etc.)
uuid-by-stringWrapper for deterministic v3/v5 from a single string input
nanoidNot a uuid but the closest alternative — see Alternatives
ulidAnother sortable-ID alternative — see Alternatives

Alternatives

LibraryTrade-off
crypto.randomUUID() (built-in)Free, zero-dep, fast, in every modern runtime since Node 14.17 / all browsers. Only emits v4. Pick when you only need random UUIDs.
nanoidShorter (21-char vs 36-char), URL-safe, ~120 bits entropy. Not RFC-compliant. Pick when bytes-on-wire matter and you don't need UUID interop.
ulidSortable, 26-char Crockford base32, 128 bits. Pre-dates UUID v7. Pick if v7 isn't yet supported by your DB driver and you need sortable IDs.
cuid2Modern collision-resistant ID; designed for distributed systems. Not RFC-compliant. Pick when ID-shape is yours alone (no third-party UUID parsers).
hyperidExtremely fast (>5M ids/sec); not RFC-compliant. Pick for very hot ID-generation paths.
@paralleldrive/cuid2Reference impl of cuid2. Same trade-off as cuid2.

Common gotchas

  1. uuid is ESM-only since v9. require("uuid") throws ERR_REQUIRE_ESM. Either upgrade your project to ESM, use dynamic import(), or pin uuid@^8.
  2. v4 vs crypto.randomUUID() — same output, different ergonomics. crypto.randomUUID() is faster (native), zero-dep, and produces an identical v4 string. Use it unless you need v5/v7 too.
  3. v5 needs a namespace. v5(value, namespace) — namespace must itself be a valid UUID string or a Uint8Array. Use one of uuid.NIL, uuid.v5.URL, uuid.v5.DNS, or your own org-specific UUID constant.
  4. v7 monotonicity is per-process. Two processes generating v7 in the same millisecond can collide on the timestamp portion (low order bits still random). Don't expect strict global monotonicity across machines.
  5. TypeScript: parse returns Uint8Array, not string. uuid.parse("...") and uuid.stringify(...) round-trip between hex string and binary form. Useful for storing in a 16-byte column.
  6. validate accepts ANY UUID version. validate(s) returns true for v1-v8 (and even the NIL UUID). Use version(s) to discriminate.

Real-world recipes

v4 — random UUID (the 99% case)

typescript
import { v4 as uuidv4 } from "uuid";

const id = uuidv4();
// "6f9619ff-8b86-d011-b42d-00c04fc964ff"

// Or use the built-in if you have Node 14.17+ / modern browser
const id2 = crypto.randomUUID();
// Identical format

Output: 36-char hyphenated lowercase. Use crypto.randomUUID() if you don't need any other version of uuid.

v5 — deterministic namespace UUID

typescript
import { v5 as uuidv5 } from "uuid";

// Built-in namespaces
const URL_NS = uuidv5.URL;     // "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
const DNS_NS = uuidv5.DNS;

const userUrlId = uuidv5("https://example.com/users/42", URL_NS);
// always the same UUID for the same URL — deterministic

// Custom org-wide namespace
const MY_ORG_NS = "f47ac10b-58cc-4372-a567-0e02b2c3d479";
const externalId = uuidv5("customer-abc-123", MY_ORG_NS);

Output:

text
userUrlId  = "a6cc5730-2261-55e2-9ad8-d3c8a8fdfa55"
externalId = "d4a8e2b1-7a0f-5e3d-8f4a-..."   // always the same for "customer-abc-123" in this namespace

v5 is the go-to for "external IDs" — map a third-party identifier (Stripe customer ID, email, account number) to a deterministic UUID so re-importing the same row produces the same UUID.

v7 — sortable timestamp-based UUID (the new standard)

typescript
import { v7 as uuidv7 } from "uuid";

const id1 = uuidv7();
await sleep(2);
const id2 = uuidv7();
await sleep(2);
const id3 = uuidv7();

console.log([id1, id2, id3]);
console.log([id1, id2, id3].sort());   // same order — v7 sorts lexicographically by creation time

Output:

text
[
  "018f5e2a-7c00-7c1d-8000-1b2c3d4e5f6a",
  "018f5e2a-7c02-7c1d-8000-2b2c3d4e5f6a",
  "018f5e2a-7c04-7c1d-8000-3b2c3d4e5f6a"
]

The first 48 bits are a Unix-ms timestamp; the next 12 bits are version/variant; the remaining 62 bits are random. Sorting v7 UUIDs gives chronological order — better B-tree locality than v4 in databases.

Use v7 instead of v4 for new primary keys in 2025+. Postgres index-locality on v7 is ~10× better than v4.

Namespace constant pattern

typescript
// src/lib/uuid-namespaces.ts — define once per project
import { v5 } from "uuid";

// Generate once with `uuidv4()` and check in — this is your org's root namespace
export const ORG_NS = "f47ac10b-58cc-4372-a567-0e02b2c3d479";

// Sub-namespaces, derived deterministically — no need to check in extras
export const USER_NS  = v5("user",  ORG_NS);
export const ORDER_NS = v5("order", ORG_NS);
export const FILE_NS  = v5("file",  ORG_NS);

// Usage
import { USER_NS } from "./uuid-namespaces";
import { v5 } from "uuid";

export function userIdFromEmail(email: string): string {
  return v5(email.toLowerCase().trim(), USER_NS);
}

Output: clean separation of namespaces with one hard-coded root UUID. Deterministic — same email → same UUID, forever.

Binary storage round-trip

typescript
import { v4, parse, stringify } from "uuid";

const id = v4();
const bytes = parse(id);          // Uint8Array(16)
const restored = stringify(bytes); // back to canonical string

// Store as bytea/blob in DB — 16 bytes vs 36 chars
console.log(bytes.length, id.length);

Output:

text
16  36

Use parse/stringify to halve UUID storage cost when your DB has a binary column type (Postgres uuid, MySQL BINARY(16)).

Validate + discriminate version

typescript
import { validate, version } from "uuid";

const ids = ["6f9619ff-8b86-d011-b42d-00c04fc964ff", "not-a-uuid"];

for (const id of ids) {
  if (!validate(id)) {
    console.log(`${id}: invalid`);
    continue;
  }
  console.log(`${id}: v${version(id)}`);
}

Output:

text
6f9619ff-8b86-d011-b42d-00c04fc964ff: v4
not-a-uuid: invalid

validate is loose (accepts all versions). version extracts the version digit — useful when migrating from v4 to v7 and you want to find the legacy rows.

Production deployment

Use v7 for new primary keys

ID typeUse case
v4Random tokens, session IDs, request IDs, anywhere sort order doesn't matter
v5Deterministic IDs from external identifiers (email, external customer ID)
v7Database primary keys; anywhere chronological sort + global uniqueness matters
v1, v6Legacy interop only; v1 leaks MAC address

Bundle size on the browser

The full uuid package tree-shakes per-version. Importing only v4:

typescript
import { v4 } from "uuid";   // ~1 KB gzipped

vs

typescript
import * as uuid from "uuid";   // ~3 KB gzipped (all versions)

For a v4-only client bundle, prefer crypto.randomUUID() — zero KB and faster.

Edge runtime compatibility

uuid uses WebCrypto's getRandomValues — works on Cloudflare Workers, Vercel Edge, Deno, Bun, browsers, Node 18+. No platform-specific code.

Performance tuning

Throughput numbers (approximate, on M1 Pro, 2026)

Functionops/sec
crypto.randomUUID() (native Node 21)~6M
uuid.v4()~2M
uuid.v7()~1.5M
uuid.v5(str, ns)~500k
nanoid()~3M

For hot paths generating millions of IDs, use crypto.randomUUID(). For v5 / v7 / v3 the uuid package has no built-in equivalent.

v7 monotonic counter

In v10+ uuid uses a monotonic counter that increments within the same millisecond — two v7() calls in the same ms always produce sortable distinct UUIDs (in the same process). Across processes, the random suffix usually avoids collision; for paranoid hot-write systems, layer per-process namespacing on top.

Avoid validate in hot paths

validate(s) runs a regex — ~5-10× slower than just trying to use the ID. For trusted internal flows skip the validation; for untrusted input keep it.

Version migration guide

v8 → v9 — CJS → ESM

This is the migration most teams confront. v8 (June 2022) is CJS; v9+ (July 2023) is ESM-only.

typescript
// v8 (CJS)
const { v4: uuidv4 } = require("uuid");
const id = uuidv4();

// v9+ (ESM)
import { v4 as uuidv4 } from "uuid";
const id = uuidv4();

// v9+ in a CJS file — dynamic import
const { v4: uuidv4 } = await import("uuid");

If you can't migrate to ESM:

json
// package.json — pin to v8
"dependencies": { "uuid": "^8.3.2" }

This works indefinitely; v8 is feature-complete and gets security backports.

v9 → v10 — v7 monotonic counter

v10 (mid-2024) added the per-process monotonic counter for v7. The output format is unchanged; you just get strict ordering within a process even at sub-millisecond intervals.

v10 → v11 — RFC 9562 compliance

v11 hardened v7 and v8 against the final RFC 9562 text (published May 2024). No code changes needed — same exports, slightly different bit-packing internally.

Type-package deprecation

The community @types/uuid package is deprecated as of v9 — uuid now bundles its own types. If you upgrade past v9, uninstall @types/uuid:

bash
npm uninstall @types/uuid

Output:

text
removed 1 package in 1s

Leaving both installed causes a duplicate-declaration error.

Security considerations

  1. v4 randomness depends on the underlying crypto source. uuid uses WebCrypto's getRandomValues — fine on all modern platforms. Don't substitute Math.random() based libraries.
  2. v1 leaks MAC address. RFC 4122 v1 embeds the node identifier (often MAC). Avoid for any public ID.
  3. v7 timestamps leak creation time. A v7 UUID encodes its creation millisecond in plaintext bytes. If creation time is sensitive (e.g. shows a user signed up at 2 AM), don't use v7 publicly. Use v4 or hash the v7.
  4. v5 with predictable namespace + value = predictable UUID. If both the namespace UUID and the input are known to an attacker, they can compute the result. Use v5 only when determinism is the goal; for opaque tokens use v4 + entropy.
  5. uuid is not cryptographically random for short prefixes. Don't use the first 8 chars of a v4 as a session token — only 32 bits of entropy. Use the full 36-char string or 128 bits.
  6. No CVEs of note. uuid has a clean security history; the only common bug-class is upstream Math.random() substitution by mistake (don't fork or copy-paste old impls).

Testing & CI integration

typescript
import { describe, it, expect } from "vitest";
import { v4, v5, v7, validate, version } from "uuid";

describe("v4", () => {
  it("produces valid UUIDs", () => {
    const id = v4();
    expect(validate(id)).toBe(true);
    expect(version(id)).toBe(4);
  });
});

describe("v5", () => {
  it("is deterministic for same namespace + value", () => {
    const NS = "f47ac10b-58cc-4372-a567-0e02b2c3d479";
    expect(v5("alice@example.com", NS)).toBe(v5("alice@example.com", NS));
  });
});

describe("v7", () => {
  it("sorts chronologically", async () => {
    const ids: string[] = [];
    for (let i = 0; i < 5; i++) {
      ids.push(v7());
      await new Promise((r) => setTimeout(r, 1));
    }
    expect([...ids].sort()).toEqual(ids);
  });
});

Output:

text
 PASS  v4 — produces valid UUIDs
 PASS  v5 — is deterministic for same namespace + value
 PASS  v7 — sorts chronologically

For deterministic-time tests, mock Date.now() so v7 timestamps are stable.

Ecosystem integrations

ToolIntegration
prisma@default(uuid()) (Prisma uses its own v4 generator) or @default(dbgenerated("gen_random_uuid()")) on Postgres
mongoosedefault: () => uuidv4() on a String field
drizzle-ormPostgres uuid() column or text("id").$default(() => v7())
typeorm@PrimaryGeneratedColumn("uuid") or supply manually
express / fastifyRequest-ID middleware (req.id = v4()) — wire to logger
pinoPino's genReqId accepts a uuid generator
next.jsServer-side; crypto.randomUUID() works in middleware/edge too

Troubleshooting common errors

  • Error [ERR_REQUIRE_ESM]: require() of ES Module ... — uuid v9+ is ESM-only. Either upgrade to ESM, use dynamic import(), or pin uuid@^8.
  • Cannot find module 'uuid' (TypeScript) — install @types/uuid for v8 and below; v9+ bundles types.
  • Subsequent property declarations must have the same type — both uuid v9+ types and @types/uuid installed. Uninstall @types/uuid.
  • v5 returns the same UUID for different inputs — usually a namespace bug (you passed a string namespace, not a UUID-format namespace). Validate the namespace with validate(NS).
  • crypto is not defined in a worker — WebCrypto missing. Polyfill or upgrade runtime (Node 18+, modern browsers, Workers all have it built-in).
  • v7 IDs not monotonic across server restarts — that's correct. The monotonic counter is per-process. For cross-process monotonicity, use a database sequence or pull from a centralised ID service.

When NOT to use this

  • You only need v4. Use crypto.randomUUID() — built-in, faster, zero-dep, same output.
  • You need short URL-friendly IDs. Use nanoid (21 chars vs 36; URL-safe by default).
  • You need IDs unique across distributed writers with strict ordering. Use a Snowflake-style generator (flake-idgen, etc.) — coordinated; doesn't rely on randomness for uniqueness.
  • Your database has a native UUID generator. Postgres gen_random_uuid(), MySQL UUID(), MongoDB ObjectId. Generate at the database — one less moving part.
  • You're storing in 16 bytes anyway. Postgres v7 generators exist as extensions; same wire format, generated at the DB.

See also