How to create a UUID in JavaScript
There is a built-in for this now, so the answer is one line. The part worth
reading is why the Math.random() one-liner that still circulates produces
something that looks identical and is not.
The built-in
crypto.randomUUID();
crypto.randomUUID() : ffa090d3-7fbd-4bf3-8d97-890afad4d395
crypto.randomUUID()
is standard in browsers and in Node, cryptographically random, and needs no
dependency.
What the digits mean
fb4dacbb-4555-4bcd-9d4c-68f06907de67
^ ^
the 13th hex digit is always 4 (the version)
the 17th is one of 8, 9, a, b (the variant)
length: 36 groups: 8-4-4-4-12
Two of the 32 hex digits are not random. The 4 says this is version 4 —
random, as opposed to the timestamp- and MAC-based version 1 — and the variant
digit says which layout the rest follows. That leaves 122 random bits, not
128.
Unique enough?
200000 generated, 200000 distinct -> 0 collisions
That run proves nothing on its own; 200,000 out of 2¹²² was never going to collide. The number that answers the question is the birthday bound: you need around 2.7 × 10¹⁸ UUIDs before a collision reaches even odds. You will not generate those.
The Math.random() version
looks fine : 36ff6e83-c8d6-4099-a130-1bcaaf63fd03
Right length, right groups, right version digit. Nothing about the output tells you it is different — which is exactly why the one-liner keeps getting pasted into places it should not be.
Math.random()
is not a cryptographic generator and does not claim to be. V8 implements it with
xorshift128+, seeded per context; given enough output, its state can be
recovered and both past and future values reconstructed.
So: fine for a DOM id or a React key. Not for a session token, a password reset link or an API key.
Its range is worth knowing too:
highest of 1,000,000 draws: 0.9999992620226952 ( always < 1 )
Math.random() returns [0, 1) — never 1. Any scaling that assumes the top of
the range is reachable is off by one case.
Building one yourself
If you need the bytes:
const b = crypto.getRandomValues(new Uint8Array(16));
b[6] = (b[6] & 0x0f) | 0x40; // version 4
b[8] = (b[8] & 0x3f) | 0x80; // variant
5d2ae888-b1e6-4ab4-b19c-92a5dfeb5511 version: 4 variant: b
The two masked bytes are what make it a valid v4 rather than 16 random bytes with dashes in them.
When a UUID is more than you need
16 random bytes as hex : d5e610b00f536b932eea7d8e5aee3018
16 random bytes base64url: bG7V6rQ4pEnAjtb1AcLCPg
Same 128 bits, fewer characters. Not a UUID, and not sortable — if you want ids that sort by creation time, look at UUIDv7 rather than inventing something.
Why it is sometimes undefined
browsers: needs a secure context (https or localhost).
crypto.randomUUID
is restricted to a secure context. On plain http:// it is simply not there,
and the error you get is a TypeError about calling undefined. That is nearly
always the reason it “does not work” — not an old browser, just a page served
over http.
About Netcup (advertisement)
The German host Netcup offers, among other things, affordable and powerful web hosting packages, KVM-based root servers and dedicated servers. With our voucher codes you can save even more (6€ off your first order, 30% off all KVM-based root servers, ...).