Short UUIDs
On this page
A UUID’s 128 bits don’t change — but the characters used to represent them can. The canonical form (36 chars with hyphens) is the longest. Here’s how to shorten it without losing data, and what each encoding is good for.
The same UUID, six different lengths
| Encoding | Length | Example |
|---|---|---|
| Canonical (hex + hyphens) | 36 | 0e6f1b8c-2c33-4f1f-9c0b-2a3d4e5f6a7b |
| Hex, no hyphens | 32 | 0e6f1b8c2c334f1f9c0b2a3d4e5f6a7b |
| Crockford base32 | 26 | 1WPK0RH1HM7CYJBC1H99TQVCXV |
| Base58 (Bitcoin alphabet) | ~22 | TZkxBfg3xnjaUDH5UQpr3i |
| Base64url (no padding) | 22 | Dm8bjCwzTx-cCyo9Tl9qew |
| Decimal integer | ~39 | 19250732086... |
All of these encode the same 16 bytes. Pick based on what you need:
- URL-safe: base64url, base58, no-hyphens hex, Crockford base32
- Double-click selectable in a terminal: any without hyphens
- Case-insensitive: Crockford base32, decimal — others are case-sensitive
- No ambiguous characters (0/O/I/l): base58, Crockford base32
Code
Base64url
URL-safe base64 with - and _ instead of + and /, padding stripped. 22 chars per UUID.
function uuidToBase64url(uuid) {
const hex = uuid.replace(/-/g, "");
const bytes = new Uint8Array(16);
for (let i = 0; i < 16; i++) bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
const b64 = btoa(String.fromCharCode(...bytes));
return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}
function base64urlToUuid(s) {
const b64 = s.replace(/-/g, "+").replace(/_/g, "/") + "==";
const binary = atob(b64);
const hex = Array.from(binary, c => c.charCodeAt(0).toString(16).padStart(2, "0")).join("");
return `${hex.slice(0,8)}-${hex.slice(8,12)}-${hex.slice(12,16)}-${hex.slice(16,20)}-${hex.slice(20)}`;
}
Crockford base32
26 chars, case-insensitive, drops I L O U to avoid confusion with 1 1 0 V. Used by ULID and many human-facing IDs.
const ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
function uuidToCrockford(uuid) {
const hex = uuid.replace(/-/g, "");
// 128 bits → 26 chars at 5 bits each (130 bits, top 2 padded)
const big = BigInt("0x" + hex);
let n = big, out = "";
for (let i = 0; i < 26; i++) {
out = ALPHABET[Number(n & 31n)] + out;
n >>= 5n;
}
return out;
}
Base58
22 chars (variable, but typically 22). Used by Bitcoin, IPFS. Drops 0 O I l for human readability.
const BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
function uuidToBase58(uuid) {
let n = BigInt("0x" + uuid.replace(/-/g, ""));
let out = "";
while (n > 0n) {
out = BASE58[Number(n % 58n)] + out;
n /= 58n;
}
return out.padStart(22, BASE58[0]);
}
For production, use a library like short-uuid (npm) or shortuuid (Python).
When to use which
Public-facing URLs → base64url or base58
https://app.example.com/share/Dm8bjCwzTx-cCyo9Tl9qew
22 chars instead of 36. Cleaner in QR codes, SMS, emails. Easy to type if needed (base58 avoids confusing chars).
Storage in databases → don’t shorten
Store as the native uuid type (16 bytes) regardless of how you display it. The display encoding lives in your application layer; the database stores 16 bytes either way.
Compact logs → no-hyphens hex
{"id":"0e6f1b8c2c334f1f9c0b2a3d4e5f6a7b","event":"login"}
4 characters shorter than canonical, still 100% recognizable as a UUID, no decoding needed.
Anywhere case matters → avoid base32 / base58
0E6F1B8C != 0e6f1b8c # base64 — case-sensitive
0E6F1B8C == 0e6f1b8c # hex / base32 — case-insensitive
If your storage or routing layer normalizes case (most databases do this for indexes; some HTTP routers do not), case-sensitive encodings can cause subtle “the URL works but the lookup fails” bugs.
Don’t try this
Truncating
const short = uuid.slice(0, 8); // ❌ 32 random bits — collisions at ~65K IDs
The first 8 hex chars of a UUID are 32 bits. The birthday-paradox 50% collision threshold drops to about 65,000 IDs. Don’t truncate UUIDs unless you’re prepared to handle collisions explicitly.
If you need a short ID, generate a nanoid at the desired length — it gives you the same security per character as a truncated UUID would, plus a wider alphabet.
Encoding as decimal
Mathematically valid, but a UUID-as-BigInt is ~39 decimal digits — longer than the canonical hex form. Only useful if you have a system that stores 128-bit integers natively (rare).
Lossy “compression”
Some libraries claim to compress UUIDs by stripping fixed bits (the version and variant nibbles). The savings are 6 bits = under 1 character — never worth the round-trip complexity.
In the wild
- YouTube’s video IDs (11 chars, base64-like) are not UUIDs but follow the same compression principle.
- Stripe IDs (
cus_abc123...) have a prefix + base62 random — also not UUIDs, but solving the same UX problem. - GitHub issue numbers (
1234) are auto-increment integers — fine for an internal scope, terrible across organizations. - Slack message permalinks use base36 timestamps + thread IDs — domain-specific compactness.
The pattern across all these: store a stable internal ID (UUID v7 in modern systems), display a compact form in URLs.
Try the tools
- UUID generator — has a “format” dropdown that includes base64
- UUID formats — full reference of every form
- UUID vs Nanoid — for when “I just need a short ID”
- UUID validator — paste a UUID in any form, decode it