🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

uniku

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

uniku

Minimal, tree-shakeable unique ID generators for every JavaScript runtime

Source
npmnpm
Version
0.3.2
Version published
Weekly downloads
15K
-31.07%
Maintainers
1
Weekly downloads
 
Created
Source

uniku

npm version npm downloads Bundle Size CI TypeScript License: MIT

One library. Every ID format. Every runtime. One runtime dependency (@noble/hashes, CUID2 only).

uniku /uˈniːku/ — Maltese for "unique"

import { uuidv7 } from 'uniku/uuid/v7'

const id = uuidv7()
// => "018e5e5c-7c8a-7000-8000-000000000000"

// Time-ordered: IDs sort by creation time
const [first, second, third] = [uuidv7(), uuidv7(), uuidv7()]
console.log(first < second && second < third) // true

At a Glance

unikuuuidtypeid-jsnanoidulidcuid2ksuidbsontsid-ts
UUID v4
UUID v7
TypeID
Nanoid
ULID
CUID2
KSUID
ObjectID
TSID
Tree-shakeable
ESM-only✅¹
Edge/Workers⚠️⚠️⚠️⚠️
Byte ↔ String-⚠️²-

Notes:

  • Byte ↔ String conversion doesn't make sense for nanoid and cuid2, since they are string-native formats with no canonical binary representation.
  • ¹ uuid@13 is ESM-only; earlier versions support CommonJS.
  • ² ulid only provides timestamp encoding/decoding, not full binary serialization.

Works Everywhere

Node.js Deno Bun Cloudflare Workers Vercel Edge Browsers

Uses globalThis.crypto (Web Crypto API) — no Node.js-specific APIs.

Performance

Generatoruniku vs npm
ULID85× faster
CUID28× faster
KSUID1.5× faster
ObjectID1.1× faster
TSID1.7× faster
UUID v71.1× faster
Nanoid~comparable speed
Nanoid (10 chars)npm is 1.1× faster
TypeID2.6× faster
UUID v4npm is 1.1× faster

Which ID Should I Use?

Quick Recommendations

Use CaseRecommendedWhy
Database primary keysUUID v7 or ULIDTime-ordered for index efficiency
API/domain identifiersTypeIDUUID v7 with readable type prefixes like user_...
URL shortenersNanoidCompact, URL-safe characters
Prevent enumerationCUID2Non-sequential, secure
Maximum compatibilityUUID v4Universal standard
Distributed systems needing sortable, URL-safe IDsULIDMillisecond ordering + 80-bit entropy
Very high-volume distributed systemsKSUIDTime-ordered with 128-bit entropy
MongoDB _id compatibilityObjectIDDrop-in match for MongoDB's native document ID format
Native 64-bit sortable integer IDTSIDFits a database BIGINT primary key, no UUID-sized overhead

Detailed Guide

UUID v4 — Use when you need maximum compatibility with existing systems.

  • 36 characters, 122-bit entropy
  • Universally supported by databases (native UUID type)
  • Not time-ordered (random distribution)
  • Best for: Legacy systems, cross-platform compatibility

UUID v7 — Use for database primary keys when you need RFC compliance.

  • 36 characters, time-ordered with millisecond precision
  • Native database support as UUID type
  • Strictly monotonic within same millisecond
  • Best for: PostgreSQL/MySQL primary keys, API identifiers

ULID — Use for database primary keys when you want URL-safe IDs.

  • 26 characters, time-ordered with millisecond precision
  • URL-safe (no hyphens), case-insensitive
  • 80-bit random entropy per millisecond
  • Best for: APIs, URLs, distributed systems needing sortability

TypeID — Use when API IDs should expose their entity type.

  • Prefix plus 26-character UUID v7 suffix, e.g. user_01h2xcejqtf2nbrexx3vqjhp41
  • Empty prefix support for canonical 26-character TypeIDs
  • Time-ordered through UUID v7, with lowercase snake_case type prefixes
  • Best for: Public API identifiers, logs, debugging, domain-specific IDs

KSUID — Use when you need highest collision resistance.

  • 27 characters, time-ordered with second precision
  • URL-safe, 128-bit random entropy (highest of all formats)
  • Best for: Very high-volume distributed systems, event sourcing

ObjectID — Use when you need drop-in compatibility with MongoDB.

  • 24-character lowercase hex, time-ordered with second precision
  • 4-byte timestamp + 5-byte per-process random + 3-byte always-incrementing counter
  • Counter never resets on a new timestamp (unlike ULID/UUIDv7/KSUID sequences)
  • Best for: MongoDB _id fields, systems already speaking the ObjectID wire format

TSID — Use when you need a native 64-bit sortable integer ID, e.g. a database BIGINT primary key.

  • 64-bit Snowflake-style value, time-ordered with millisecond precision
  • Returns a bigint by default (the only uniku generator that isn't string-primary); toString/fromString convert to/from the 13-character canonical string
  • 42-bit timestamp + 10-bit node + 12-bit per-millisecond counter, with a persistent lazily-random node ID
  • Best for: relational database BIGINT primary keys, high-throughput distributed ID generation without central coordination

Nanoid — Use for short, URL-friendly identifiers.

  • 21 characters (configurable), 126-bit entropy
  • Customizable alphabet and length
  • Not time-ordered
  • Best for: URL shorteners, session tokens, invite codes

CUID2 — Use when ID unpredictability matters.

  • 24 characters (configurable), hash-based
  • Non-sequential, prevents enumeration attacks
  • Not time-ordered
  • Best for: User-facing IDs where security matters

Binary Serialization: When to Use toBytes()

Formats with binary representations (UUID, ULID, KSUID, ObjectID, TSID) support toBytes() and fromBytes() for efficient storage:

// Store as BINARY(16) instead of VARCHAR(36)
const bytes = uuidv7.toBytes(id)  // 16 bytes
db.insert({ id: bytes })

// Retrieve and convert back
const id = uuidv7.fromBytes(row.id)

Benefits of binary storage:

  • Up to 50% smaller: E.g., 16 bytes vs 36 characters for UUID
  • Faster indexing: Binary comparison is faster than string comparison
  • Reduced I/O: Less data transferred between app and database

When to use binary:

  • High-volume tables (millions of rows)
  • Performance-critical queries on ID columns
  • Storage cost is a concern

Exception — tsid: toBytes()/fromBytes() operate on bigint, not string, since bigint is TSID's primary type (see tsid in the API Reference below). Every other generator's toBytes()/fromBytes() operate on its primary string type.

When to keep strings:

  • Debugging/logging convenience
  • API responses (humans read them)
  • Small tables where savings are negligible
FormatString LengthBinary SizeSavings
UUID v4/v736 chars16 bytes56%
ULID26 chars16 bytes38%
TypeIDprefix + 27 chars16 bytesprefix-dependent
KSUID27 chars20 bytes26%
ObjectID24 chars12 bytes50%
TSID13 chars*8 bytes38%
Nanoid21 charsN/A-
CUID224 charsN/A-

Note: Nanoid and CUID2 don't have binary representations because they're string-native formats with no canonical byte encoding.

* TSID's primary type is bigint, not a string; the 13-character figure is its canonical toString() form, shown here only for a size comparison against the other formats' native strings.

Installation

# pnpm (recommended)
pnpm add uniku

# bun
bun add uniku

# npm
npm install uniku

# yarn
yarn add uniku

# deno
deno install npm:uniku

Bundle Impact

ImportMinified + gzipped
uniku/uuid/v4~1.1 KB
uniku/uuid/v7~1.4 KB
uniku/ulid~1.8 KB
uniku/typeid~1.6 KB
uniku/cuid/v2~992 B*
uniku/cuid2~1007 B*
uniku/nanoid~1.1 KB
uniku/ksuid~1.3 KB
uniku/objectid~1.3 KB
uniku/tsid~1.4 KB
uniku/generators~98 B
  • The CUID2 entry point imports SHA3-512 from @noble/hashes; this table's entry-point size excludes that external dependency.

Quick Start

UUID v4 (random)

import { uuidv4 } from 'uniku/uuid/v4'

const id = uuidv4()
// => "550e8400-e29b-41d4-a716-446655440000"

// Convert to/from bytes
const bytes = uuidv4.toBytes(id)
const str = uuidv4.fromBytes(bytes)

UUID v7 (time-ordered)

import { uuidv7 } from 'uniku/uuid/v7'

const id = uuidv7()
// => "018e5e5c-7c8a-7000-8000-000000000000"

// IDs are lexicographically sortable by creation time
const ids = [uuidv7(), uuidv7(), uuidv7()]
ids.sort() // Already in creation order

ULID (time-ordered)

import { ulid } from 'uniku/ulid'

const id = ulid()
// => "01HW9T2W9W9YJ3JZ1H4P4M2T8Q"

// Convert to/from bytes
const bytes = ulid.toBytes(id)
const str = ulid.fromBytes(bytes)

TypeID (prefixed UUID v7)

import { typeid } from 'uniku/typeid'

const id = typeid('user')
// => "user_01h2xcejqtf2nbrexx3vqjhp41"

const uuid = typeid.toUuid(id)
const restored = typeid.fromUuid('user', uuid)

const canonical = typeid('')
// => "01h2xcejqtf2nbrexx3vqjhp41"

CUID2 (secure, non-time-ordered)

import { cuidv2 } from 'uniku/cuid/v2'

const id = cuidv2()
// => "pfh0haxfpzowht3oi213cqos"

// Custom length
const shortId = cuidv2({ length: 10 })
// => "tz4a98xxat"

// Validation (type guard)
if (cuidv2.isValid(maybeId)) {
  console.log(maybeId) // TypeScript knows this is a string
}

uniku/cuid/v2 is the canonical entry point. The older uniku/cuid2 import still works and re-exports the same generator, but is now deprecated — prefer import { cuidv2 } from 'uniku/cuid/v2'.

Nanoid (URL-friendly, custom alphabet)

import { nanoid } from 'uniku/nanoid'

const id = nanoid()
// => "V1StGXR8_Z5jdHi6B-myT"

// Custom size
const shortId = nanoid(10)
// => "IRFa-VaY2b"

// Custom alphabet and size via options
const hexId = nanoid({ alphabet: '0123456789abcdef', size: 12 })
// => "4f90d13a42bc"

KSUID (time-ordered, high entropy)

import { ksuid } from 'uniku/ksuid'

const id = ksuid()
// => "2QnJjKLvpSfpZqGiPPxVwWLMy2p"

// Convert to/from bytes
const bytes = ksuid.toBytes(id)
const str = ksuid.fromBytes(bytes)

// Extract timestamp (milliseconds)
const ts = ksuid.timestamp(id)

ObjectID (time-ordered, MongoDB-compatible)

import { objectid } from 'uniku/objectid'

const id = objectid()
// => "667c3f2a1e2b3c4d5e6f7081"

// Convert to/from bytes
const bytes = objectid.toBytes(id)
const str = objectid.fromBytes(bytes)

// Extract timestamp (milliseconds)
const ts = objectid.timestamp(id)

TSID (time-ordered, 64-bit integer)

import { tsid } from 'uniku/tsid'

// Generate a TSID bigint (the primary type, not a string)
const id = tsid()
// => 862301223059968074n

// Convert to/from the canonical 13-character string
const str = tsid.toString(id)
// => "0QXW2CK4XZM2A"
tsid.fromString(str) === id // true

// Extract timestamp (milliseconds, full precision)
const ts = tsid.timestamp(id)

// Convert to/from 8 bytes
const bytes = tsid.toBytes(id)
const restored = tsid.fromBytes(bytes)

Migrating to uniku

From uuid

- import { v4 as uuidv4 } from 'uuid'
+ import { uuidv4 } from 'uniku/uuid/v4'

From nanoid

- import { nanoid } from 'nanoid'
+ import { nanoid } from 'uniku/nanoid'

From ulid

- import { ulid } from 'ulid'
+ import { ulid } from 'uniku/ulid'

From @paralleldrive/cuid2

- import { createId } from '@paralleldrive/cuid2'
+ import { cuid2 } from 'uniku/cuid2'

From @owpz/ksuid

- import { KSUID } from '@owpz/ksuid'
+ import { ksuid } from 'uniku/ksuid'

- const id = KSUID.random().toString()
+ const id = ksuid()

- const parsed = KSUID.parse(str)
- const timestamp = parsed.timestamp
+ const timestamp = ksuid.timestamp(str)

From bson

- import { ObjectId } from 'bson'
+ import { objectid } from 'uniku/objectid'

- const id = new ObjectId().toHexString()
+ const id = objectid()

- const timestamp = new ObjectId(str).getTimestamp().getTime()
+ const timestamp = objectid.timestamp(str)

API Reference

uuidv4 (from uniku/uuid/v4)

uuidv4(options?: UuidV4Options): string
uuidv4(options: UuidV4Options | undefined, buf: Uint8Array, offset?: number): Uint8Array
uuidv4.toBytes(id: string): Uint8Array
uuidv4.fromBytes(bytes: Uint8Array): string
uuidv4.isValid(id: unknown): id is string
uuidv4.NIL  // "00000000-0000-0000-0000-000000000000"
uuidv4.MAX  // "ffffffff-ffff-ffff-ffff-ffffffffffff"

uuidv7 (from uniku/uuid/v7)

uuidv7(options?: UuidV7Options): string
uuidv7(options: UuidV7Options | undefined, buf: Uint8Array, offset?: number): Uint8Array
uuidv7.toBytes(id: string): Uint8Array
uuidv7.fromBytes(bytes: Uint8Array): string
uuidv7.timestamp(id: string): number
uuidv7.isValid(id: unknown): id is string
uuidv7.NIL  // "00000000-0000-0000-0000-000000000000"
uuidv7.MAX  // "ffffffff-ffff-ffff-ffff-ffffffffffff"

ulid (from uniku/ulid)

ulid(options?: UlidOptions): string
ulid(options: UlidOptions | undefined, buf: Uint8Array, offset?: number): Uint8Array
ulid.toBytes(id: string): Uint8Array
ulid.fromBytes(bytes: Uint8Array): string
ulid.timestamp(id: string): number
ulid.isValid(id: unknown): id is string
ulid.NIL  // "00000000000000000000000000"
ulid.MAX  // "7ZZZZZZZZZZZZZZZZZZZZZZZZZ"

typeid (from uniku/typeid)

typeid(prefix: string, options?: TypeidOptions): string
typeid.toBytes(id: string): Uint8Array
typeid.fromBytes(prefix: string, bytes: Uint8Array): string
typeid.toUuid(id: string): string
typeid.fromUuid(prefix: string, uuid: string): string
typeid.timestamp(id: string): number
typeid.prefix(id: string): string
typeid.suffix(id: string): string
typeid.isValid(id: unknown): id is string

Use an empty prefix (typeid('')) to generate Jetify-compatible prefixless TypeIDs.

cuidv2 (from uniku/cuid/v2)

cuidv2(options?: CuidV2Options): string
cuidv2.isValid(id: unknown): id is string

Also available (deprecated) as cuid2 from uniku/cuid2 — the same generator under the pre-versioned entry point.

nanoid (from uniku/nanoid)

nanoid(): string
nanoid(size: number): string
nanoid(options: NanoidOptions): string
nanoid.isValid(id: unknown): id is string

// Constant
URL_ALPHABET  // "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"

ksuid (from uniku/ksuid)

ksuid(options?: KsuidOptions): string
ksuid(options: KsuidOptions | undefined, buf: Uint8Array, offset?: number): Uint8Array
ksuid.toBytes(id: string): Uint8Array
ksuid.fromBytes(bytes: Uint8Array): string
ksuid.timestamp(id: string): number
ksuid.isValid(id: unknown): id is string
ksuid.NIL  // "000000000000000000000000000"
ksuid.MAX  // "aWgEPTl1tmebfsQzFP4bxwgy80V"

objectid (from uniku/objectid)

objectid(options?: ObjectIdOptions): string
objectid(options: ObjectIdOptions | undefined, buf: Uint8Array, offset?: number): Uint8Array
objectid.toBytes(id: string): Uint8Array
objectid.fromBytes(bytes: Uint8Array): string
objectid.timestamp(id: string): number
objectid.isValid(id: unknown): id is string
objectid.NIL  // "000000000000000000000000"
objectid.MAX  // "ffffffffffffffffffffffff"

tsid (from uniku/tsid)

tsid(options?: TsidOptions): bigint
tsid(options: TsidOptions | undefined, buf: Uint8Array, offset?: number): Uint8Array
tsid.toBytes(id: bigint): Uint8Array
tsid.fromBytes(bytes: Uint8Array): bigint
tsid.toString(id: bigint): string
tsid.fromString(str: string): bigint
tsid.timestamp(id: bigint, epoch?: number): number
tsid.isValid(id: unknown): id is bigint
tsid.NIL  // 0n
tsid.MAX  // 18446744073709551615n

Unlike every other generator listed here, tsid() returns bigint by default, and toBytes/fromBytes/timestamp/isValid all operate on that bigint. toString/fromString are the boundary conversions to/from the 13-character canonical Crockford Base32 string.

Documentation

For advanced usage, examples, and contributing guidelines, see the full documentation on GitHub. The uniku@1.x stability contract defines the supported entry points, runtimes, and release gates.

Third-party libraries that inspired this project:

  • uuid: the most popular UUID library for JavaScript
  • ulid: the reference ULID implementation for JavaScript
  • TypeID-JS: official JavaScript implementation of TypeID
  • @paralleldrive/cuid2: secure, collision-resistant IDs
  • @owpz/ksuid: K-Sortable Unique Identifier
  • bson: official MongoDB BSON library, including the ObjectId implementation
  • nanoid: tiny, URL-friendly unique string ID generator
  • tsid-ts: TypeScript implementation of TSID, a Snowflake-style 64-bit Time-Sorted Unique Identifier

Other:

Author

Hi, I'm Alberto Schiabel, aka @jkomyno. You can follow me on:

License

MIT — see LICENSE

Keywords

uuid

FAQs

Package last updated on 13 Jul 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts