Generate UUID v4 in JavaScript β€” Code Guide + Examples

Β·TypeScript & Full-stack DeveloperΒ·Reviewed byMarcus WebbΒ·Published

Use the free online UUID v4 Generator directly in your browser β€” no install required.

Try UUID v4 Generator Online β†’

Every JavaScript application eventually needs unique identifiers β€” session tokens, database rows, idempotency keys for payment APIs, correlation IDs for distributed tracing. The simplest way to generate UUID v4 in JavaScript today is crypto.randomUUID(): zero dependencies, one line, cryptographically secure. UUID v4 is widely used precisely because it needs no coordination between services β€” client and server can both produce IDs independently without collision risk. This guide covers that built-in API, the uuid npm package, validation β€” all on Node.js 19+ and modern browsers. For a no-code option, the UUID v4 Generator on ToolDeck produces compliant identifiers instantly.

  • βœ“crypto.randomUUID() is built into browsers and Node.js β€” zero dependencies, one line of code.
  • βœ“UUID v4 is a 128-bit random identifier: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx (y is 8, 9, a, or b).
  • βœ“Validate with /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i β€” checks version and variant bits.
  • βœ“The uuid npm package adds v1, v3, v5, and v7 support when you need more than random IDs.
  • βœ“For database primary keys, prefer UUID v7 (time-ordered) over v4 (random) to reduce index fragmentation.

What is UUID v4?

A UUID (Universally Unique Identifier) version 4 is a 128-bit random identifier formatted as 32 hexadecimal digits separated by four hyphens: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx. The 4 at position 15 marks the version. The y character at position 20 is one of 8, 9, a, or b (the RFC 4122 variant). The remaining 122 bits are random. UUID v4 is the most common version in JavaScript applications because it requires no coordination between systems β€” you can generate IDs independently on client and server without worrying about collisions.

Before Β· javascript
After Β· javascript
// No identifier
const event = { action: "user.login", ts: 1711824000 };
// With UUID v4
const event = {
  id: "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
  action: "user.login",
  ts: 1711824000
};

crypto.randomUUID() β€” The Native JavaScript Approach

crypto.randomUUID() is available in Chrome 92+, Firefox 95+, Safari 15.4+, and Node.js 19+ (with experimental support from Node.js 14.17.0 behind globalThis.crypto). It returns a lowercase 36-character UUID v4 string. No import needed in browser JavaScript. In Node.js, you can call it directly on the global crypto object or import it explicitly from the node:crypto module.

JavaScript β€” browser (zero imports)
// Works in any modern browser β€” no build step, no bundler
const requestId = crypto.randomUUID();
console.log(requestId);
// "3e7f1a92-4b0c-4d8e-9f12-7a6b3c8d5e1f"

// Use it wherever you need a unique identifier
const telemetryEvent = {
  event_id: crypto.randomUUID(),
  action: "checkout.started",
  session_id: crypto.randomUUID(),
  timestamp: Date.now(),
  cart_total_cents: 14999,
};
console.log(JSON.stringify(telemetryEvent, null, 2));
Node.js β€” two equivalent approaches
// Approach 1: global crypto (Node.js 19+)
const orderId = crypto.randomUUID();
console.log(orderId);
// "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"

// Approach 2: explicit import from node:crypto
import { randomUUID } from 'node:crypto';
const correlationId = randomUUID();
console.log(correlationId);
// "f7e6d5c4-b3a2-4190-8f7e-6d5c4b3a2190"

Feature-detecting crypto.randomUUID is worth doing if your code might run in older browsers or embedded WebViews. The check is a single typeof guard:

JavaScript β€” feature detection with fallback
function generateUUIDv4() {
  // Prefer the native API when available
  if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
    return crypto.randomUUID();
  }

  // Manual fallback using getRandomValues (see next section)
  const bytes = new Uint8Array(16);
  crypto.getRandomValues(bytes);

  // Set version (4) and variant (RFC 4122)
  bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
  bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10xx

  const hex = [...bytes].map(b => b.toString(16).padStart(2, '0'));
  return [
    hex.slice(0, 4).join(''),
    hex.slice(4, 6).join(''),
    hex.slice(6, 8).join(''),
    hex.slice(8, 10).join(''),
    hex.slice(10, 16).join(''),
  ].join('-');
}

console.log(generateUUIDv4());
// "9b2e4f1a-7c3d-4e8f-a5b6-0d2c1e9f8a7b"
Note:In secure contexts (HTTPS pages, localhost, browser extensions), crypto.randomUUID() is always available. It throws on non-secure HTTP pages in some browsers. If your application ever runs on plain HTTP during development, the getRandomValues fallback above handles that case.

Generate UUID v4 Without a Library

Sometimes you cannot rely on crypto.randomUUID() β€” maybe you are targeting a WebView that strips the crypto API, or you need to understand what happens under the hood. The manual approach uses crypto.getRandomValues() (available since IE 11) to fill 16 bytes with random data, then applies two bitmask operations to set the version and variant fields. These two operations are the only difference between UUID v4 and a pure random byte string.

JavaScript β€” manual UUID v4 with getRandomValues
function uuidv4Manual() {
  const bytes = new Uint8Array(16);
  crypto.getRandomValues(bytes);

  // Set version: bits 12–15 of byte 6 = 0100 (version 4)
  bytes[6] = (bytes[6] & 0x0f) | 0x40;

  // Set variant: bits 6–7 of byte 8 = 10 (RFC 4122)
  bytes[8] = (bytes[8] & 0x3f) | 0x80;

  const hex = [...bytes].map(b => b.toString(16).padStart(2, '0'));

  return (
    hex.slice(0, 4).join('') + '-' +
    hex.slice(4, 6).join('') + '-' +
    hex.slice(6, 8).join('') + '-' +
    hex.slice(8, 10).join('') + '-' +
    hex.slice(10, 16).join('')
  );
}

const traceId = uuidv4Manual();
console.log(traceId);
// "e4d7c2a1-3f9b-48e5-a612-9d8c7b6a5f4e"

// Verify it passes UUID v4 validation
const UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
console.log(UUID_V4_RE.test(traceId)); // true
Warning:Never use Math.random() to generate UUIDs. It is not cryptographically secure, its output period is too short, and some engines produce predictable sequences. Always use crypto.getRandomValues() or crypto.randomUUID().

The uuid npm Package β€” Multi-Version Support

The uuid package on npm has been the go-to UUID library for JavaScript since before crypto.randomUUID() existed. It still makes sense in three situations: you need UUID versions other than v4 (v1, v3, v5, v7), you target runtimes older than Node.js 14.17.0, or you want the validate and parse utility functions. For plain UUID v4 on a modern runtime, the native API is enough and I would skip the dependency.

bash β€” install
npm install uuid
JavaScript β€” uuid package for v4 generation
import { v4 as uuidv4, validate, version } from 'uuid';

// Generate a UUID v4
const paymentId = uuidv4();
console.log(paymentId);
// "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed"

// Validate any UUID string
console.log(validate(paymentId));    // true
console.log(validate("not-a-uuid")); // false

// Detect the version of a UUID
console.log(version(paymentId));     // 4

// Generate a batch for seeding a test database
const testAccounts = Array.from({ length: 5 }, () => ({
  account_id: uuidv4(),
  plan: "starter",
  created_at: new Date().toISOString(),
}));
console.log(testAccounts);
Note:The uuid package internally uses crypto.getRandomValues() in browsers and crypto.randomBytes() in Node.js, so the entropy source is identical to the native API. The difference is just the additional utility functions and multi-version support.

If you prefer not to write code at all, try the UUID v4 Generator β€” it generates RFC 4122-compliant v4 identifiers directly in your browser with one click.

Deterministic UUIDs β€” Generating UUID v5 from a String

UUID v4 is random by definition β€” calling it twice always gives different results. Sometimes you need the opposite: the same input string should always produce the same UUID. That is what UUID v5 does. It hashes a namespace UUID and an input string with SHA-1, then formats the result as a UUID. Same namespace + same input = same output, every time, on every machine. This is useful for deriving stable IDs from URLs, email addresses, or any string that already identifies a resource.

JavaScript β€” uuid v5 for deterministic IDs
import { v5 as uuidv5 } from 'uuid';

// Built-in namespace for URLs (RFC 4122)
const URL_NAMESPACE = uuidv5.URL;
// "6ba7b811-9dad-11d1-80b4-00c04fd430c8"

// Same URL always produces the same UUID
const pageId1 = uuidv5("https://api.warehouse.dev/products/sku-7291", URL_NAMESPACE);
const pageId2 = uuidv5("https://api.warehouse.dev/products/sku-7291", URL_NAMESPACE);
console.log(pageId1 === pageId2); // true
console.log(pageId1);
// "a6e4e1c0-7e23-5d3b-8f14-9c2a1b3d5e7f"

// Custom namespace for your application
const APP_NAMESPACE = "f47ac10b-58cc-4372-a567-0e02b2c3d479";
const tenantId = uuidv5("acme-corp", APP_NAMESPACE);
console.log(tenantId);
// "d4735e3a-265b-564e-8f32-7a1b2c3d4e5f"

Quick note: UUID v3 does the same thing but with MD5 instead of SHA-1. Prefer v5 for new projects. MD5 has known collision weaknesses, and while that does not matter much for ID generation, there is no reason to choose it over SHA-1 when both are available.

The native crypto.randomUUID() takes no arguments β€” it returns a string and nothing else. Use it when you need RFC 4122 compliant identifiers with zero dependencies. When you need raw random bytes instead of a formatted UUID string β€” for example, to fill a typed array or derive a key β€” reach for crypto.getRandomValues() directly. The related APIs that matter for UUID work are listed below.

Property / Method
Return Type
Description
crypto.randomUUID()
string
Returns a 36-character UUID v4 string in lowercase hexadecimal with hyphens
crypto.getRandomValues(arr)
TypedArray
Fills a typed array with cryptographically random values β€” the building block for manual UUID generation
URL.createObjectURL(blob)
string
Generates a unique blob URL (not a UUID, but sometimes confused with one)

UUID v4 regex pattern breakdown:

Segment
Pattern
Meaning
[0-9a-f]{8}
xxxxxxxx
First 8 hex digits β€” 32 random bits
[0-9a-f]{4}
xxxx
Next 4 hex digits β€” 16 random bits
4[0-9a-f]{3}
4xxx
Version nibble fixed to 4, followed by 12 random bits
[89ab][0-9a-f]{3}
yxxx
Variant nibble is one of 8, 9, a, b β€” followed by 12 random bits
[0-9a-f]{12}
xxxxxxxxxxxx
Final 12 hex digits β€” 48 random bits

Validate UUID v4 with Regex

Validating that a string is a proper UUID v4 comes up constantly β€” incoming API request bodies, URL parameters, webhook payloads. A hand-rolled regex is the right choice when you want zero dependencies and are only validating v4. If you are already using the uuid package, prefer its validate() export instead β€” it handles all UUID versions and is less error-prone than maintaining a custom pattern. The regex /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i checks both the version nibble (must be 4) and the variant nibble (must be 8, 9, a, or b). Use RegExp.prototype.test() for boolean checks and .match() when you need to extract a UUID from surrounding text.

JavaScript β€” UUID v4 validation helper
const UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;

function isUUIDv4(str) {
  return UUID_V4_RE.test(str);
}

// Valid UUID v4
console.log(isUUIDv4("9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d")); // true

// UUID v1 β€” version nibble is 1, not 4
console.log(isUUIDv4("550e8400-e29b-11d4-a716-446655440000")); // false

// UUID v7 β€” version nibble is 7
console.log(isUUIDv4("018e4a0c-5b3f-7d12-8a9b-0c1d2e3f4a5b")); // false

// Malformed strings
console.log(isUUIDv4("not-a-uuid"));           // false
console.log(isUUIDv4(""));                      // false
console.log(isUUIDv4("9b1deb4d3b7d4bad9bdd2b0d7b3dcb6d")); // false (no hyphens)

// Extract UUID from a larger string
const logLine = 'Request req_id=9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d failed with 503';
const match = logLine.match(/[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/i);
console.log(match?.[0]);
// "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"

Generate and Attach UUIDs from File and API Response

In practice, you rarely call crypto.randomUUID() in isolation. Two patterns come up constantly: assigning IDs to records before writing them to a database, and attaching correlation IDs to outbound API requests so you can trace a request across services in your logs.

Read NDJSON File β†’ Assign UUIDs β†’ Write Back

Node.js β€” assign UUIDs to records from a file
import { readFileSync, writeFileSync } from 'node:fs';
import { randomUUID } from 'node:crypto';

function assignIds(inputPath, outputPath) {
  const lines = readFileSync(inputPath, 'utf-8')
    .split('\n')
    .filter(line => line.trim());

  const records = lines.map(line => {
    try {
      const record = JSON.parse(line);
      if (!record.id) {
        record.id = randomUUID();
      }
      return JSON.stringify(record);
    } catch (err) {
      console.error(`Skipping malformed line: ${err.message}`);
      return null;
    }
  }).filter(Boolean);

  writeFileSync(outputPath, records.join('\n') + '\n');
  console.log(`Assigned IDs to ${records.length} records β†’ ${outputPath}`);
}

assignIds('warehouse-products.ndjson', 'warehouse-products-with-ids.ndjson');
// Assigned IDs to 1284 records β†’ warehouse-products-with-ids.ndjson

Attach Correlation ID to Outbound API Request

Node.js β€” correlation ID for distributed tracing
import { randomUUID } from 'node:crypto';

async function createShipment(orderPayload) {
  const correlationId = randomUUID();

  try {
    const response = await fetch('https://api.logistics.dev/v2/shipments', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Correlation-ID': correlationId,
        'X-Idempotency-Key': randomUUID(),
      },
      body: JSON.stringify(orderPayload),
    });

    if (!response.ok) {
      const errorBody = await response.text();
      throw new Error(`Shipment API returned ${response.status}: ${errorBody}`);
    }

    const result = await response.json();
    console.log(`Shipment created: ${result.shipment_id} (correlation: ${correlationId})`);
    return result;
  } catch (err) {
    console.error(`[correlation:${correlationId}] Shipment failed: ${err.message}`);
    throw err;
  }
}

await createShipment({
  order_id: "ord_8a3f91bc",
  destination: { city: "Portland", state: "OR", zip: "97201" },
  items: [{ sku: "WH-7291", quantity: 2, weight_kg: 1.4 }],
});

Command-Line UUID Generation

You do not always need a script. Node.js can generate a UUID directly from the command line, which is handy for shell scripts, CI pipelines, and quick ad-hoc testing. The -e flag evaluates a single expression.

bash β€” generate UUID from the command line
# Single UUID
node -e "console.log(crypto.randomUUID())"
# 3e7f1a92-4b0c-4d8e-9f12-7a6b3c8d5e1f

# Five UUIDs at once
node -e "for(let i=0;i<5;i++) console.log(crypto.randomUUID())"

# Generate and assign to a shell variable
export REQUEST_ID=$(node -e "process.stdout.write(crypto.randomUUID())")
echo "Request ID: $REQUEST_ID"

# Use npx uuid (if you have the package installed globally or want a one-off)
npx uuid v4
# 1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed
bash β€” generate UUID in a browser console (no Node.js)
# Open any browser DevTools console and type:
crypto.randomUUID()
# "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
Note:If you need UUIDs in a CI pipeline and Node.js is not available, uuidgen is pre-installed on macOS and most Linux distributions. It generates UUID v4 by default on modern systems.

High-Performance Alternative β€” nanoid

If you are generating thousands of IDs per second and the 36-character UUID format is not a hard requirement, consider nanoid. It is 2x faster than uuid.v4(), produces a 21-character URL-safe ID by default, and weighs about 1 KB after minification. The output is not a UUID β€” it is a custom-alphabet ID β€” so do not use it where RFC 4122 compliance is required (database UUID columns, APIs that validate UUID format, OpenTelemetry trace IDs). But for internal correlation IDs, React component keys, and URL slugs, it is a good fit.

bash β€” install nanoid
npm install nanoid
JavaScript β€” nanoid for short unique IDs
import { nanoid, customAlphabet } from 'nanoid';

// Default: 21-char URL-safe ID (A-Za-z0-9_-)
const trackingCode = nanoid();
console.log(trackingCode);
// "V1StGXR8_Z5jdHi6B-myT"

// Custom length
const shortCode = nanoid(10);
console.log(shortCode);
// "IRFa-VaY2b"

// Custom alphabet β€” numbers only, 12 digits
const numericId = customAlphabet('0123456789', 12);
console.log(numericId());
// "839274651023"

// Custom alphabet β€” hex only, 32 chars (same entropy as UUID v4 without hyphens)
const hexId = customAlphabet('0123456789abcdef', 32);
console.log(hexId());
// "4f8a1b2c3d7e9f0a5b6c8d1e2f3a4b5c"

Terminal Output with Syntax Highlighting

Debugging UUID-heavy applications means staring at walls of hex strings in the terminal. Color-coding helps. The chalk library (or the newer built-in node:util styleText in Node.js 21.7+) lets you highlight UUIDs in log output so they stand out from surrounding text.

Node.js β€” color-coded UUID output with chalk
import chalk from 'chalk';
import { randomUUID } from 'node:crypto';

function logEvent(action, metadata = {}) {
  const eventId = randomUUID();
  const timestamp = new Date().toISOString();

  console.log(
    chalk.gray(timestamp),
    chalk.cyan(eventId),
    chalk.white(action),
    Object.keys(metadata).length
      ? chalk.dim(JSON.stringify(metadata))
      : ''
  );
}

logEvent('shipment.created', { order_id: 'ord_8a3f', carrier: 'fedex' });
logEvent('payment.captured', { amount_cents: 14999, currency: 'USD' });
logEvent('webhook.delivered', { endpoint: 'https://hooks.acme.dev/orders' });
// 2026-03-27T10:15:00.000Z  a1b2c3d4-...  shipment.created  {"order_id":"ord_8a3f",...}
Warning:Color escape codes corrupt log files and break JSON parsers. Only use chalk or styleText for terminal output that a human reads directly. For structured logs that go to a file or log aggregator, use plain JSON.

Generate Short Unique IDs in JavaScript

A 36-character UUID is sometimes too long β€” URL slugs, QR code data, SMS messages, and embedded hardware protocols all have length constraints.

JavaScript β€” three techniques for shorter IDs
import { randomUUID } from 'node:crypto';

// 1. Strip hyphens from UUID v4 β†’ 32-char hex string
const hex32 = randomUUID().replaceAll('-', '');
console.log(hex32);
// "3e7f1a924b0c4d8e9f127a6b3c8d5e1f" (32 chars)

// 2. Base64-encode 16 random bytes β†’ 22-char string (URL-safe)
const bytes = new Uint8Array(16);
crypto.getRandomValues(bytes);
const base64Id = Buffer.from(bytes)
  .toString('base64url')
  .replace(/=+$/, '');
console.log(base64Id);
// "Pj8akksNTY6fEnarPIvR" (22 chars, 128 bits of entropy)

// 3. nanoid at custom length
import { nanoid } from 'nanoid';
const short12 = nanoid(12);
console.log(short12);
// "V1StGXR8_Z5j" (12 chars, ~71 bits of entropy)

The collision probability math: a 12-character nanoid (default alphabet of 64 characters) gives roughly 71 bits of entropy. At 1,000 IDs per second, you would need about 116 years to reach a 1% collision probability. For most applications, that is more than enough. If you are generating millions of IDs per day, stick with the full UUID or use nanoid with at least 21 characters.

UUID v7 β€” Time-Ordered Alternative to v4

UUID v7 (defined in RFC 9562) embeds a 48-bit Unix millisecond timestamp in the first segment, followed by random bits. The result is a UUID that looks similar to v4 but sorts chronologically. This makes it a better choice than v4 for database primary keys: new rows always land at the end of the B-tree index instead of at random positions, which reduces page splits and fragmentation. In projects where I need time-ordered IDs for a Postgres table, I switch to v7 immediately β€” the index performance difference is measurable at scale. The UUID v7 Generator on ToolDeck shows the embedded timestamp for any v7 UUID.

JavaScript β€” UUID v7 with the uuid package
import { v7 as uuidv7 } from 'uuid';

// Generate three UUID v7 values β€” notice they sort chronologically
const id1 = uuidv7();
const id2 = uuidv7();
const id3 = uuidv7();

console.log(id1);
// "018e4a0c-5b3f-7d12-8a9b-0c1d2e3f4a5b"
console.log(id2);
// "018e4a0c-5b40-7e34-9c2d-1e4f5a6b7c8d"
console.log(id3);
// "018e4a0c-5b41-7f56-ae3f-2a5b6c7d8e9f"

// They sort lexicographically by creation time
console.log([id3, id1, id2].sort());
// [id1, id2, id3] β€” chronological order preserved

// Use v4 for tokens where timing should not be leaked
import { v4 as uuidv4 } from 'uuid';
const sessionToken = uuidv4(); // fully random, no timing info
Note:The uuid package supports v7 from version 9.0.0 onwards. If you are on an older version, run npm install uuid@latest to upgrade.

UUID v4 in the Browser Without a Build Step

No bundler, no npm, no transpiler. Just a plain HTML file. This is the simplest possible way to generate a UUID in client-side JavaScript. It works because crypto.randomUUID() is a built-in browser API.

HTML β€” minimal browser UUID generator
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><title>UUID Generator</title></head>
<body>
  <p>Your UUID: <strong id="output"></strong></p>
  <button onclick="document.getElementById('output').textContent = crypto.randomUUID()">
    Generate
  </button>
  <script>
    // Generate one on page load
    document.getElementById('output').textContent = crypto.randomUUID();
  </script>
</body>
</html>

That is the entire file. No CDN imports, no script tags pulling in libraries. For anything more complex β€” batch generation, validation, deterministic IDs β€” you will want the uuid package or the manual fallback shown earlier. But for a quick prototype or internal tool, this is all you need.

Common Mistakes

I've seen the Math.random() UUID pattern copied from old blog posts into production code more often than I'd like to admit. The bugs these patterns introduce are silent: no runtime error, just subtly wrong behavior that surfaces later under load or in security reviews.

❌ Using Math.random() to generate UUIDs

Problem: Math.random() is not cryptographically secure. Its output is predictable in some engines, and the low entropy makes collisions far more likely than a proper CSPRNG.

Fix: Always use crypto.randomUUID() or crypto.getRandomValues(). Both use the operating system's CSPRNG.

Before Β· JavaScript
After Β· JavaScript
// INSECURE β€” predictable, low entropy
function badUuid() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
    /[xy]/g,
    c => {
      const r = Math.random() * 16 | 0;
      return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    }
  );
}
// SECURE β€” uses the OS CSPRNG
const id = crypto.randomUUID();

// Or if you need a manual fallback:
function secureUuid() {
  const bytes = new Uint8Array(16);
  crypto.getRandomValues(bytes);
  bytes[6] = (bytes[6] & 0x0f) | 0x40;
  bytes[8] = (bytes[8] & 0x3f) | 0x80;
  const h = [...bytes].map(b => b.toString(16).padStart(2, '0'));
  return `${h.slice(0,4).join('')}-${h.slice(4,6).join('')}-${h.slice(6,8).join('')}-${h.slice(8,10).join('')}-${h.slice(10).join('')}`;
}
❌ Comparing UUIDs with case-sensitive equality

Problem: crypto.randomUUID() returns lowercase hex, but UUIDs from other systems (databases, APIs, user input) might use uppercase. Direct === comparison fails when cases differ.

Fix: Normalize both sides to lowercase before comparing.

Before Β· JavaScript
After Β· JavaScript
const fromApi = "9B1DEB4D-3B7D-4BAD-9BDD-2B0D7B3DCB6D"; // uppercase from API
const local  = "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d";  // lowercase from crypto

if (fromApi === local) { /* never runs */ }
const fromApi = "9B1DEB4D-3B7D-4BAD-9BDD-2B0D7B3DCB6D";
const local  = "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d";

if (fromApi.toLowerCase() === local.toLowerCase()) {
  // matches correctly
}
❌ Storing UUIDs as objects instead of strings

Problem: Some UUID libraries (especially in other languages) return UUID objects. In JavaScript, accidentally wrapping a UUID string in an object breaks equality checks, JSON serialization, and database queries.

Fix: Always store and pass UUIDs as plain strings. If a library returns an object, call .toString() or access the string property immediately.

Before Β· JavaScript
After Β· JavaScript
// Creating unnecessary wrapper
class UUID {
  constructor(value) { this.value = value; }
}
const id = new UUID(crypto.randomUUID());
console.log(id === id); // true, but...
console.log(JSON.stringify({ id })); // {"id":{"value":"..."}}
// Just use a string
const id = crypto.randomUUID();
console.log(JSON.stringify({ id }));
// {"id":"3e7f1a92-4b0c-4d8e-9f12-7a6b3c8d5e1f"}
❌ Treating UUID v4 as sortable

Problem: UUID v4 is fully random. Sorting by UUID v4 gives arbitrary order, not creation order. This leads to unpredictable pagination, confusing admin UIs, and bad database index performance.

Fix: Use UUID v7 when you need time-ordered identifiers. Keep UUID v4 for tokens and correlation IDs where sort order is irrelevant.

Before Β· JavaScript
After Β· JavaScript
// Bad: using v4 as a sortable primary key
const rows = [
  { id: crypto.randomUUID(), created: "2026-03-27" },
  { id: crypto.randomUUID(), created: "2026-03-26" },
];
rows.sort((a, b) => a.id.localeCompare(b.id));
// Sort order is random β€” NOT by creation date
import { v7 as uuidv7 } from 'uuid';
// Good: v7 sorts by creation time
const rows = [
  { id: uuidv7(), created: "2026-03-27" },
  { id: uuidv7(), created: "2026-03-26" },
];
rows.sort((a, b) => a.id.localeCompare(b.id));
// Sort order matches creation time

crypto.randomUUID() vs uuid vs nanoid β€” Quick Comparison

Method
Output Format
UUID Versions
Bundle Size
Custom Types
Requires Install
crypto.randomUUID()
36-char UUID v4
v4 only
0 KB
N/A
No (built-in)
uuid npm package
36-char UUID
v1, v3, v4, v5, v6, v7
~6.5 KB
N/A
npm install
nanoid
21-char URL-safe ID
Custom (not UUID)
~1 KB
N/A
npm install
Manual getRandomValues
36-char UUID v4
v4 only
0 KB
N/A
No (built-in)
crypto.randomBytes (Node)
Buffer β†’ 36-char UUID v4
v4 only
0 KB
N/A
No (Node built-in)
uuidv7 npm package
36-char UUID v7
v7 only
~2 KB
N/A
npm install

For most JavaScript projects: use crypto.randomUUID() when you only need UUID v4 and your runtime is recent enough. Reach for the uuid package when you need v5 (deterministic) or v7 (time-ordered) support. Use nanoid when a short, URL-safe ID is more practical than a 36-character UUID β€” but remember that nanoid output is not UUID-compliant and will fail validation on any system expecting RFC 4122 format.

For a no-code alternative, try the UUID v4 Generator to create identifiers instantly in the browser. To inspect an existing UUID, paste it into the UUID Decoder to see its version, variant, and any embedded timestamp data.

Frequently Asked Questions

How do I generate a UUID v4 in JavaScript?

Call crypto.randomUUID() in any modern browser (Chrome 92+, Firefox 95+, Safari 15.4+) or Node.js 19+. It returns a lowercase string like "3e7f1a92-4b0c-4d8e-9f12-7a6b3c8d5e1f". No import is needed in browsers because crypto is a built-in global; in Node.js you can also use import { randomUUID } from "node:crypto" to be explicit about the module. For runtimes older than Node.js 19 you can still call crypto.randomUUID() on the global β€” it was available experimentally from Node.js 14.17.0. If you need a no-code option, the UUID v4 Generator at /en/uuid/v4 produces compliant identifiers in one click.

JavaScript
const sessionId = crypto.randomUUID();
console.log(sessionId);
// "3e7f1a92-4b0c-4d8e-9f12-7a6b3c8d5e1f"

Is crypto.randomUUID() cryptographically secure?

Yes. crypto.randomUUID() uses the same CSPRNG (cryptographically secure pseudorandom number generator) that backs crypto.getRandomValues(). The 122 random bits in a UUID v4 make collision probability negligible for all practical purposes β€” you would need to generate roughly 2.71 quintillion UUIDs before reaching a 50% chance of a single collision. The entropy comes from the operating system's random source (/dev/urandom on Linux, BCryptGenRandom on Windows), not from Math.random(), which is explicitly not cryptographically secure. This means UUID v4 values are safe to use as session tokens, CSRF tokens, and other security-sensitive identifiers where predictability must be avoided. Never substitute Math.random()-based UUID generators in security contexts.

Can I use UUID v4 as a database primary key?

You can, but there is a meaningful performance trade-off. UUID v4 is fully random, so B-tree indexes fragment badly because each new row inserts at a random position in the index rather than at the end. On write-heavy tables this causes excessive page splits and cache misses, which degrades INSERT and range-scan performance. If your database supports it (PostgreSQL, MySQL 8.0+, SQL Server), UUID v7 (time-ordered) is a better primary key because new rows always append near the end of the index. UUID v4 remains the right choice for session tokens, OAuth state parameters, idempotency keys, correlation IDs, and any field where hiding creation time is desirable.

JavaScript
// UUID v4 β€” random, not sortable
const correlationId = crypto.randomUUID();

// UUID v7 β€” time-ordered, better for DB primary keys
import { v7 as uuidv7 } from 'uuid';
const rowId = uuidv7();

What is the difference between UUID v4 and UUID v7?

UUID v4 fills 122 bits with random data β€” every segment is effectively noise. UUID v7 (RFC 9562, published 2024) encodes a 48-bit Unix millisecond timestamp in the first segment, 12 bits of sub-millisecond precision in the second segment, and random bits in the remainder. Both are 128 bits total and use the same 36-character hyphenated format, so they are interchangeable at the storage layer. UUID v7 is lexicographically sortable by creation time, which keeps B-tree indexes compact and makes range queries on time windows efficient. Choose UUID v4 when the ID must not reveal when it was created β€” for example, public-facing tokens where timing information could be exploited β€” and UUID v7 for database primary keys, audit logs, and event streams where chronological order matters.

How do I validate a UUID v4 string in JavaScript?

Test against the regex /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i. The literal "4" in position 15 (0-indexed) confirms the version nibble; the character at position 20 β€” one of 8, 9, a, or b β€” encodes the RFC 4122 variant bits. This regex correctly rejects UUID v1, v7, and any malformed string. The i flag makes it case-insensitive, so uppercase hex digits from other systems pass validation without normalization. If you only need to know whether a string is any valid UUID (regardless of version), use the looser pattern /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i instead.

JavaScript
const UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;

UUID_V4_RE.test("550e8400-e29b-41d4-a716-446655440000"); // true
UUID_V4_RE.test("550e8400-e29b-11d4-a716-446655440000"); // false (v1)
UUID_V4_RE.test("not-a-uuid"); // false

How do I generate a short unique ID in JavaScript?

Strip hyphens from a UUID v4 to get a 32-character hex string: crypto.randomUUID().replaceAll("-", ""). For something even shorter, use nanoid which produces a 21-character URL-safe ID (A–Z, a–z, 0–9, _ and -) by default with comparable collision resistance to a full UUID. The trade-off is straightforward: shorter IDs have higher collision probability, but nanoid at 21 characters still provides 126 bits of entropy, which is more than sufficient for virtually every real-world application. For URL slugs and QR code payloads you can go as short as 10–12 characters with nanoid before collision probability becomes a concern at typical generation rates. Avoid Base64-encoding a raw UUID and truncating it β€” truncation destroys the statistical independence of the bits and makes collisions harder to reason about.

JavaScript
// 32-char hex string from UUID v4
const hexId = crypto.randomUUID().replaceAll('-', '');
// "3e7f1a924b0c4d8e9f127a6b3c8d5e1f"

// 21-char URL-safe ID via nanoid
import { nanoid } from 'nanoid';
const shortId = nanoid();
// "V1StGXR8_Z5jdHi6B-myT"
Also available in:Python
SL
Sophie LaurentTypeScript & Full-stack Developer

Sophie is a full-stack developer focused on TypeScript across the entire stack β€” from React frontends to Express and Fastify backends. She has a particular interest in type-safe API design, runtime validation, and the patterns that make large JavaScript codebases stay manageable. She writes about TypeScript idioms, Node.js internals, and the ever-evolving JavaScript module ecosystem.

MW
Marcus WebbTechnical Reviewer

Marcus specialises in JavaScript performance, build tooling, and the inner workings of the V8 engine. He has spent years profiling and optimising React applications, working on bundler configurations, and squeezing every millisecond out of critical rendering paths. He writes about Core Web Vitals, JavaScript memory management, and the tools developers reach for when performance really matters.