TechEarl

Generate a UUID in JavaScript: crypto.randomUUID() (Browser and Node)

Generate a UUID in JavaScript with the built-in crypto.randomUUID(), no library needed. The secure-context gotcha that returns undefined on plain http, how it works in the browser vs Node, and when you need a v7 library instead.

Ishan Karunaratne⏱️ 7 min readUpdated
Share thisCopied
Generate a UUID in JavaScript with the native crypto.randomUUID(), the secure-context requirement that breaks it on plain http, browser vs Node usage, and the v4 vs v7 distinction.

To generate a UUID in JavaScript in 2026, use the built-in crypto.randomUUID(). No library, no npm install, one line:

javascript
const id = crypto.randomUUID();
// "36b8f84d-df4e-4d49-b662-bb00a661f0ad"

That returns a fresh RFC 4122 version 4 (random) UUID as a 36-character string every time you call it. It is part of the Web Crypto API, ships in every current browser and in Node, and uses a cryptographically secure random source, so you do not need uuid, nanoid, or a hand-rolled Math.random() generator for the common case.

The David Walsh post that a lot of people still land on warned that "browser support is very patchy." That was true years ago. It is no longer true, and that single stale sentence is why people still reach for a library out of habit. crypto.randomUUID() is now Baseline: Chrome and Edge 92, Firefox 95, Safari 15.4, and it has been there since Node 14.17. The one real catch is not support, it is the secure-context rule below.

The #1 gotcha: it needs a secure context

Here is the trap that sends people back to a library after crypto.randomUUID returns undefined or throws on their dev box: the whole crypto object is only populated in a secure context. That means HTTPS, or http://localhost (and 127.0.0.1, and file://), but not a plain http:// origin. Open your app at http://192.168.1.50:3000 or http://staging.internal over plain HTTP and crypto.randomUUID is not a function.

javascript
// On https:// or http://localhost  → works
crypto.randomUUID();          // "36b8f84d-..."

// On a plain http:// origin (LAN IP, internal hostname):
typeof crypto;                // "object", but...
crypto.randomUUID;            // undefined  → "crypto.randomUUID is not a function"

localhost is treated as secure by the spec, which is why it works in dev and then breaks the moment a colleague hits your machine by IP. The fix is not a polyfill, it is to serve the page over HTTPS (a self-signed cert for the LAN, or a tunnel). If you genuinely cannot, see the fallback at the end. This secure-context requirement is also the answer to nearly every "why is crypto.randomUUID undefined" question on the web.

In Node.js

Node exposes the same API, with a version wrinkle worth knowing. Since Node 19, crypto is a global the same way it is in the browser, so the browser line works unchanged:

javascript
// Node 19+: crypto is global, no import
const id = crypto.randomUUID();

On any Node from 14.17 onward (which is to say, anything you should still be running), import it explicitly. This is the form I default to in a module because it does not depend on the Node version having the global:

javascript
import { randomUUID } from "node:crypto";
const id = randomUUID();

CommonJS is the same idea with require:

javascript
const { randomUUID } = require("node:crypto");
const id = randomUUID();

There is no secure-context concept in Node, so the HTTPS rule above is purely a browser thing. If you see crypto.randomUUID is not a function in Node, you are on a version older than 14.17 and the answer is to upgrade Node, not to add a dependency. As a bonus, the native call benchmarks faster than the userland uuid package's v4(), because it drops straight into the runtime's CSPRNG.

It generates a v4 UUID, and sometimes that's the wrong one

crypto.randomUUID() always returns a v4 UUID: 122 random bits, no embedded timestamp, no machine info. Collisions are not "impossible," but the probability is small enough to ignore for any realistic application, so treating it as unique is fine.

What v4 is bad at is ordering. Because it is fully random, consecutive v4 UUIDs land all over the place. Use one as a primary key and every insert scatters across the B-tree index, which fragments the index and hurts write throughput on a busy table. The fix is v7, a newer UUID format whose leading bits are a millisecond timestamp, so freshly minted IDs sort roughly in creation order and insert sequentially. The platform does not generate v7 yet (crypto.randomUUID() is v4 only, and Node has no native v7 as of mid-2026), so for time-ordered IDs you do still need a library, for example the uuid package's v7() or uuidv7.

If the reason you want a UUID is a database key, the version choice matters more than the generation syntax. I cover the storage and indexing trade-offs in depth in how to store a UUID in MySQL (the BINARY(16) vs CHAR(36) decision, and where v7 earns its keep) and storing a UUID in PostgreSQL with the native uuid type.

Fallback for ancient or insecure-context environments

You should almost never need this. If you are stuck on a plain-HTTP origin you cannot upgrade, or supporting a runtime old enough to lack randomUUID, build a v4 UUID from crypto.getRandomValues, which has slightly wider reach and is still cryptographically secure:

javascript
function teRandomUuid() {
  const bytes = crypto.getRandomValues(new Uint8Array(16));
  bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
  bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 1
  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("")}`;
}

Note this still needs crypto.getRandomValues, which is also secure-context-gated in the browser, so it does not rescue a plain-HTTP page. It only helps where getRandomValues exists but randomUUID does not. Never substitute Math.random() here: it is not cryptographically secure and is the wrong tool for an identifier.

FAQ

See also

Sources

Authoritative references this article was fact-checked against.

TagsJavaScriptUUIDGUIDcrypto.randomUUIDWeb CryptoNode.jsunique id

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

Run JavaScript When a CSS Animation or Transition Ends

Fire a JavaScript callback when a CSS transition or animation finishes, using the transitionend and animationend events. The propertyName filtering, the bubbling trap, the cases where the event never fires, and the modern Web Animations API promise alternative.