Skip to content

ULID Generator — TypeScript source

Generate Universally Unique Lexicographically Sortable Identifiers (ULID) - 26-character Crockford-base32 strings that sort by millisecond timestamp. Paste any ULID to decode its timestamp and randomness. Runs entirely in your browser.

This is the TypeScript implementation — the same logic the interactive tool runs, in a shareable, citable form.

// ULID generator backed by the `ulid` package. Injectable time + RNG for
// deterministic tests. The unit-test surface for the ULID Generator tool.

import { ulid, decodeTime, isValid } from 'ulid';

export interface UlidOptions {
  timestamp?: number; // ms since epoch; defaults to now
  rng?: () => number; // [0, 1)
}

/** Generate a ULID, optionally pinned to a timestamp and/or custom RNG. */
export function generateUlid(opts: UlidOptions = {}): string {
  return opts.rng ? ulid(opts.timestamp, opts.rng) : ulid(opts.timestamp);
}

/** Decode the embedded timestamp (ms since epoch) from a ULID. */
export function decodeUlidTime(id: string): number {
  return decodeTime(id);
}

export interface UlidInfo {
  valid: boolean;
  /** Why the input is not a valid ULID (present only when invalid). */
  error?: string;
  /** First 10 chars — the Crockford-base32 encoded timestamp. */
  timePart?: string;
  /** Last 16 chars — the random part. */
  randomness?: string;
  /** Decoded timestamp, ms since epoch (48-bit ceiling: 281474976710655). */
  timestamp?: number;
  /** Timestamp as ISO-8601 UTC. */
  iso?: string;
}

/** Inspect a pasted ULID: validate and break out time part, randomness, timestamp. */
export function inspectUlid(input: string): UlidInfo {
  const id = input.trim().toUpperCase();
  if (id.length !== 26) {
    return { valid: false, error: `Length must be 26 characters, got ${id.length}` };
  }
  // The package's own isValid covers the Crockford charset (its decode table
  // is the alphabet's single source of truth — no third regex copy here).
  if (!isValid(id)) {
    return { valid: false, error: 'Invalid character — ULIDs use Crockford base32 (no I, L, O, U)' };
  }
  try {
    const timestamp = decodeTime(id);
    return {
      valid: true,
      timePart: id.slice(0, 10),
      randomness: id.slice(10),
      timestamp,
      iso: new Date(timestamp).toISOString(),
    };
  } catch {
    return { valid: false, error: 'Time part overflows the 48-bit ULID timestamp ceiling' };
  }
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →