micro-memoize

A blazing-fast, tiny memoization library with configurable LRU caching, expiration, and cache-event hooks.

Library
npm
v5.2.0
263stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
68/100Good
Development Activity72
Maintenance72
Community48
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
83/100Excellent
Architecture85
Code Quality82
Innovation78
Learning Curve85

micro-memoize is a lightweight JavaScript/TypeScript memoization library that wraps any function in a cache-backed proxy, returning stored results for repeated argument combinations instead of recomputing them. It ships as ESM, CJS, and UMD builds with full TypeScript types, and its only runtime dependencies are fast-equals and fast-stringify.

Beyond simple single-value memoization, it supports LRU caches of arbitrary size, custom key equality (exact, shallow, or deep), key serialization, argument-count limits, time-based expiration, forced cache-busting, and an event emitter for observing cache hits, additions, updates, and deletions. Benchmarks bundled in the repo show it consistently outperforming lodash, ramda, underscore, mem, memoizee, memoizerific, and several other popular memoization packages across a range of argument shapes.

What You Get

  • Drop-in memoize(fn, options) wrapper with full TypeScript inference for the return type and cache shape
  • Configurable LRU cache via maxSize, from single-entry (the default) up to any bound, with automatic least-recently-used eviction
  • Multiple key-equality strategies: exact (Object.is), shallow, deep, a custom comparator, or full-key serialization
  • Time-based expires option with dynamic/conditional expiration and async promise-rejection auto-eviction
  • A cache.on('add' | 'delete' | 'hit' | 'update') event emitter for observability, plus opt-in named statsName usage tracking
  • ESM, CJS, and UMD builds with zero-config TypeScript types

Common Use Cases

  • Caching expensive pure computations (parsing, formatting, derived-data transforms) keyed by their arguments
  • Memoizing selector/derived-state functions in state-management layers to avoid recomputation on unrelated updates
  • Wrapping async data-fetching functions with async: true so failed promises are automatically evicted instead of poisoning the cache
  • Building bounded caches for hot paths where only the last N distinct inputs matter, via maxSize
  • Instrumenting cache hit/miss rates in production via statsName and the stats API to validate memoization is actually helping

Under The Hood

Architecture The public surface is a single memoize() factory in src/index.ts that constructs a Cache instance (src/Cache.ts) and returns a wrapped function; when maxSize is left at its default of 1, a dedicated single-entry code path (cache.z) skips the linked-list traversal, recency updates, and size bookkeeping that the general multi-entry path (cache.g/cache.u/cache.n) performs, so the common case never pays for LRU machinery it doesn’t need. Optional concerns are opt-in and lazily instantiated: ExpirationManager (src/expires.ts) and StatsManager (src/stats.ts) are only created when options.expires or options.statsName is set, and a CacheEventEmitter is only attached the first time cache.on() is called, so a plain memoized function carries none of that overhead. Key transformation composes transformKey, maxArgs (src/maxArgs.ts), and serialize (src/serialize.ts) into a single reduced function applied in a fixed, documented order, keeping the cache’s core get/set logic decoupled from how keys are shaped.

Tech Stack Written in TypeScript (about 89% of the codebase) compiled via Rollup into ESM, CJS, and UMD builds, each with its own generated .d.ts/.d.mts/.d.cts types declared through a conditional exports map in package.json. The only runtime dependencies are fast-equals (for shallow/deep key-item comparison) and fast-stringify (for the serialize option); everything else — @planttheidea/build-tools, release-it, tinybench, and a long list of competing memoization libraries (lodash, ramda, underscore, mem, memoizee, memoizerific, lru-memoize, fast-memoize) — is a devDependency used for building, releasing, or benchmarking against alternatives. It targets Node 6+ and older browsers (IE9+/Safari 6+) despite the modern TypeScript-first source.

Code Quality The __tests__ directory has a dedicated Vitest test file per feature area (async, expires, forceUpdate, isKeyEqual, isKeyItemEqual, maxArgs, serialize, stats, plus the core index), giving each configuration option its own focused suite rather than one monolithic test file. ESLint runs with typescript-eslint and eslint-plugin-import at --max-warnings=0, Prettier enforces formatting, and tsc --noEmit gates types; internal fields use short, single/double-letter property names (c, h, t, k, o, p, s) for minification, each documented with a JSDoc comment spelling out what the abbreviation stands for. No CI workflow configuration is present in the repository itself, so gating appears to rely on local scripts (release:scripts) run as part of the release process rather than a hosted pipeline.

What Makes It Unique The defining design choice is specializing the overwhelmingly common single-entry cache (maxSize: 1, the default) into its own code path that never touches the linked list at all, rather than treating every cache size uniformly through one general algorithm — most comparable libraries do not split behavior this way. Combined with fully opt-in expiration, stats, and event-emitter subsystems that add zero cost unless explicitly configured, this keeps the default, most-used configuration extremely lean while still supporting a comprehensive feature set (LRU, TTL, async-aware eviction, custom equality, key transforms) for use cases that need it. The repository’s own benchmark suite, pitting it against a dozen well-known alternatives across primitive/array/object and single/multi-argument shapes, is used to substantiate the performance claims rather than asserting them without evidence.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search