To generate a UUID in JavaScript in 2026, use the built-in crypto.randomUUID(). No library, no npm install, one line:
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.
// 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:
// 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:
import { randomUUID } from "node:crypto";
const id = randomUUID();CommonJS is the same idea with require:
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:
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
- How to store a UUID in MySQL: the
BINARY(16)vsCHAR(36)decision and the v4-vs-v7 index-fragmentation problem. - Storing a UUID in PostgreSQL: using the native
uuidtype instead of a text column. - Modern JavaScript array methods: the 2026-current toolkit (
at,flat,toReversed,Object.groupBy) and the mutation trap. - JavaScript object methods:
keys,values,entries,fromEntries,freeze, and null-prototype maps.
Sources
Authoritative references this article was fact-checked against.
- Crypto: randomUUID() method (MDN)developer.mozilla.org
- Secure contexts (MDN)developer.mozilla.org
- Node.js crypto.randomUUID (official docs)nodejs.org
- Node.js global crypto (official docs)nodejs.org





