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, ...).