Type-safe, prefixed IDs like user_a1b2c3 for TypeScript & JavaScript — always know what an ID belongs to.
prefID generates short, unique IDs that carry a prefix telling you what they belong to — user_a1b2c3, order_9f8e7d. The prefix makes IDs readable in logs, URLs, and your database, and TypeScript understands it, so you can never mix a user ID up with an order ID.
import { id } from "prefid";
id("user"); // => "user_a8Kd0f2bQ1nR7pZ3xW4mT6y"
id("order"); // => "order_9f8e7d6c5b4a3F2e1D0cB9aX"Need IDs that sort by creation time? sortableId puts a time component up front,
so a plain string sort is also a chronological sort — the idea behind ULID and
UUIDv7, but keeping prefID's prefix and type. Great for database keys, cursors,
and correlating logs, without a central sequence:
import { sortableId, getTimestamp, getTimestampOrThrow, getDate } from "prefid";
sortableId("evt"); // => "evt_00VQ5a1k0lBjgjfx6pwYy6WkY"
sortableId("evt"); // => "evt_00VQ5a1mgkWGzAvv93g1bC3yR" ← later, and sorts after
// IDs created in the same millisecond (or if the clock steps backwards) are
// still strictly increasing — monotonic by default.
// Read the embedded time back out:
getTimestamp("evt_00VQ5a1k0lBjgjfx6pwYy6WkY"); // => 1721600000000 (ms), or undefined if malformed
getTimestampOrThrow("evt_00VQ5a1k…"); // same, but throws on a malformed id instead of returning undefined
getDate("evt_00VQ5a1k…"); // => Date, or undefined if malformed/out of rangePrefer a case-insensitive, unambiguous alphabet (like ULID)? Use the exported
BASE32_CROCKFORD preset — it drops the look-alike letters I, L, O, U:
import { createSortableId, BASE32_CROCKFORD } from "prefid";
const eventId = createSortableId({ alphabet: BASE32_CROCKFORD });
eventId("evt"); // => "evt_00VQ5A1K0MBJGJFX6PWYY6WKY" — no 0/O or 1/l confusionnpm install prefid- 🏷️ Self-describing — the prefix tells you what an ID is at a glance.
- 🧠 Type-safe —
id("user")has the type`user_${string}`, so passing the wrong ID type is a compile error. - 🔀 Sortable option —
sortableId("evt")embeds a time component so IDs sort chronologically as plain strings (ULID / UUIDv7-style), with no central coordinator. - 🟨 JavaScript too — TypeScript is optional. Works the same in plain JS; types are a bonus, not a requirement.
- 🔒 Secure — the random part uses the platform's cryptographic RNG, never
Math.random(). - 🪶 Zero dependencies — tiny and focused on one job.
- 🌍 Universal — works in Node 14.18+, browsers, Deno, Bun, and edge runtimes. Ships ESM + CommonJS.
prefID works in plain JavaScript just as well as in TypeScript — the published package is already compiled JavaScript, so there's nothing to compile on your end:
// CommonJS
const { id } = require("prefid");
// or ESM: import { id } from "prefid";
id("user"); // "user_a8Kd0f2bQ1nR7pZ3xW4mT6y"Everything (id, createId, template, ensureUnique, isId, getPrefix) runs identically in JS. TypeScript users get one extra thing on top — the typed prefix (`user_${string}`) that catches wrong-ID mistakes at compile time. JS users still get editor autocomplete from the bundled type definitions.
prefid runs anywhere with a cryptographic RNG, and picks the right source automatically:
| Runtime | Random source |
|---|---|
| Node.js 14.18+ (ESM + CommonJS) | node:crypto |
| Browsers, Deno, Bun, edge runtimes | globalThis.crypto |
Node 14.18 is the minimum because it's the first release with require("node:crypto"). On Node ESM versions that don't expose a global crypto (14–19), a Node-specific entry point (selected automatically via the package's exports conditions) sources randomness from node:crypto — so ESM works there too.
Full guides, examples, and the complete API live on the documentation site:
| Guide | |
|---|---|
| Quick Start | Generate your first ID |
id() |
The default generator |
createId() |
Configure size, separator, alphabet |
sortableId() |
Time-ordered IDs (ULID / UUIDv7-style) |
getTimestamp() & getDate() |
Read the time back out of a sortable ID (getTimestampOrThrow for a strict variant) |
template() |
Custom ID layouts (INV-####-####) |
ensureUnique() |
Guarantee an ID is free in your store |
isId() & getPrefix() |
Validate and read IDs |
| Uniqueness & Collisions | How safe the IDs are |
| Comparison | vs uuid and nanoid |
Contributions are welcome! See CONTRIBUTING.md to get started.
MIT © Syed Suhail Ahmed