Skip to content
100% in your browser. Nothing you paste is uploaded — all processing runs locally. Read more →

JavaScript & TypeScript UUIDs

On this page
  1. v4 in one line
  2. Other versions: the uuid package
  3. TypeScript: typing UUIDs
  4. React / Next.js
  5. Browser-only (no bundler) v7
  6. Validation
  7. Common pitfalls
  8. Cheat sheet
  9. Try the tools

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:

RuntimeAvailable since
Modern browsers (Chrome, Firefox, Safari, Edge)2022
Node.js (global)19.0
Node.js (require("crypto").randomUUID())14.17
BunAll versions
DenoAll versions
Cloudflare WorkersAll 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

Cheat sheet

GoalCode
Random UUID (browsers, Node 19+, Bun)crypto.randomUUID()
Random UUID (older Node)require("crypto").randomUUID()
Time-ordered (DB key)import { v7 } from "uuid"
Deterministicimport { v5 } from "uuid"
Validateimport { validate } from "uuid"
TypeScript brandtype UUID = string & { __brand: "UUID" }

Try the tools