Skip to main content

es-toolkit

es-toolkit utility library guide for modern JavaScript/TypeScript. Use when: (1) Writing utility functions (array manipulation, object transforms, string conversion, math operations, type checks), (2) Migrating from lodash, (3) Needing debounce/throttle/retry, (4) Working with async primitives (Mutex, Semaphore, delay), (5) Any code that could use es-toolkit instead of custom implementation. Triggers on: es-toolkit, lodash, utility function, debounce, throttle, groupBy, pick, omit, merge, cloneDeep, snakeCase, camelCase, isNil, isEqual, retry, Mutex, Semaphore, attempt. Do NOT use for: general JavaScript questions unrelated to utilities, or React/framework-specific code.

Source facts

Repository
pedronauck/devstack
Last source activity
March 15, 2026 at 20:11
Detected SKILL.md language
English
Stars
9
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
3 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
es-toolkit
description
es-toolkit utility library guide for modern JavaScript/TypeScript. Use when: (1) Writing utility functions (array manipulation, object transforms, string conversion, math operations, type checks), (2) Migrating from lodash, (3) Needing debounce/throttle/retry, (4) Working with async primitives (Mutex, Semaphore, delay), (5) Any code that could use es-toolkit instead of custom implementation. Triggers on: es-toolkit, lodash, utility function, debounce, throttle, groupBy, pick, omit, merge, cloneDeep, snakeCase, camelCase, isNil, isEqual, retry, Mutex, Semaphore, attempt. Do NOT use for: general JavaScript questions unrelated to utilities, or React/framework-specific code.
# es-toolkit Usage Guide es-toolkit is a modern JavaScript utility library — 2-3x faster and up to 97% smaller than lodash. It has 100% test coverage, built-in TypeScript types, and supports Node.js 18+, Deno, Bun, and browsers. ## Import Rules ALWAYS use subpath imports for tree-shaking. NEVER import from the top-level barrel. ```typescript // CORRECT — subpath imports (tree-shakeable) import { groupBy } from "es-toolkit/array"; import { debounce } from "es-toolkit/function"; import { pick, omit } from "es-toolkit/object"; import { snakeCase } from "es-toolkit/string"; import { sum } from "es-toolkit/math"; import { isNil } from "es-toolkit/predicate"; import { delay, Mutex, Semaphore } from "es-toolkit/promise"; import { attempt } from "es-toolkit/util"; import { mapKeys } from "es-toolkit/map"; import { keyBy } from "es-toolkit/set"; // ALSO CORRECT — top-level for classes/quick scripts import { Mutex, Semaphore } from "es-toolkit"; // CORRECT — compat layer (lodash-compatible API) import { get, set, template } from "es-toolkit/compat"; // WRONG — never do this import _ from "es-toolkit"; import { groupBy } from "es-toolkit/dist/array"; ``` ## Decision Guide Before writing any utility function, check this hierarchy: 1. **Native JS/TS** — Use native when it's equally readable and performant - `Array.prototype.map/filter/reduce/find/some/every/flat/flatMap` - `Object.keys/values/entries/assign/fromEntries` - `structuredClone()` for deep cloning (but `cloneDeep` handles edge cases better) - `?.` optional chaining instead of `get(obj, 'a.b.c')` 2. **es-toolkit** — Use when native is verbose, error-prone, or missing the operation - Array: `groupBy`, `sortBy`, `orderBy`, `uniq`, `uniqBy`, `difference`, `intersection`, `union`, `chunk`, `zip`, `shuffle`, `sample` - Object: `pick`, `omit`, `pickBy`, `omitBy`, `merge`, `toMerged`, `cloneDeep`, `invert` - String: `camelCase`, `snakeCase`, `kebabCase`, `capitalize`, `pascalCase`, `constantCase`, `deburr`, `escapeRegExp` - Function: `debounce`, `throttle`, `once`, `memoize`, `retry`, `flow`, `flowRight`, `noop` - Math: `sum`, `sumBy`, `mean`, `meanBy`, `round`, `clamp`, `inRange`, `random`, `randomInt` - Predicate: `isNil`, `isNotNil`, `isEqual`, `isEqualWith`, `isEmpty`, `isPlainObject` - Promise: `delay`, `timeout`, `Mutex`, `Semaphore` - Util: `attempt`, `attemptAsync`, `invariant` 3. **es-toolkit/compat** — Only for lodash migration or when you need exact lodash behavior - `get`, `set`, `has`, `template`, `iteratee`, `matches`, `property` - Prefer native `?.` over `get()` — compat `get` is slower due to path parsing ## Quick Reference — Most Used Functions ### Array ```typescript import { groupBy, sortBy, uniqBy, difference, chunk, zip, sample, countBy, flatMapDeep, orderBy, } from "es-toolkit/array"; groupBy(items, item => item.category); // Record<string, T[]> sortBy(users, [u => u.age, u => u.name]); // T[] — stable multi-key sort orderBy(users, ["age", "name"], ["asc", "desc"]); // T[] — with direction control uniqBy(items, item => item.id); // T[] — unique by key difference([1, 2, 3], [2, 3]); // [1] chunk([1, 2, 3, 4, 5], 2); // [[1,2], [3,4], [5]] zip(["a", "b"], [1, 2]); // [["a",1], ["b",2]] sample([1, 2, 3]); // random element countBy([1, 2, 3, 4], n => (n % 2 === 0 ? "even" : "odd")); // { even: 2, odd: 2 } ``` ### Object ```typescript import { pick, omit, merge, toMerged, cloneDeep, pickBy, omitBy, invert, clone, } from "es-toolkit/object"; pick(user, ["name", "email"]); // { name, email } omit(user, ["password", "secret"]); // everything except those keys merge(target, source); // deep merge (MUTATES target) toMerged(target, source); // deep merge (returns NEW object) cloneDeep(complexObj); // deep clone (handles Date, RegExp, Map, Set) pickBy(obj, (value, key) => value != null); // pick by predicate invert({ a: "1", b: "2" }); // { "1": "a", "2": "b" } ``` ### Function ```typescript import { debounce, throttle, once, retry, flow, memoize, noop, identity, } from "es-toolkit/function"; // Debounce with cancel/flush and AbortSignal support const search = debounce(query => fetchResults(query), 300); search.cancel(); search.flush(); // Debounce with leading edge const leading = debounce(fn, 300, { edges: ["leading"] }); // Throttle const onScroll = throttle(handleScroll, 100); // Retry with exponential backoff const data = await retry(fetchData, { retries: 5, delay: attempt => Math.min(100 * 2 ** attempt, 5000), shouldRetry: (err, attempt) => err.status >= 500, signal: controller.signal, }); // Function composition pipeline const transform = flow(trim, toLowerCase, split(" "), filterEmpty); // Execute only once const initialize = once(() => expensiveSetup()); ``` ### Promise & Concurrency ```typescript import { delay, timeout, Mutex, Semaphore } from "es-toolkit/promise"; await delay(1000); // wait 1 second const result = await timeout(fetchData(), 5000); // timeout after 5s // Mutex — single concurrent task const mutex = new Mutex(); await mutex.acquire(); try { /* critical section */ } finally { mutex.release(); } // Semaphore — N concurrent tasks const sem = new Semaphore(3); await sem.acquire(); try { /* max 3 concurrent */ } finally { sem.release(); } ``` ### String ```typescript import { camelCase, snakeCase, kebabCase, pascalCase, capitalize, constantCase, deburr, escapeRegExp, } from "es-toolkit/string"; camelCase("foo-bar"); // "fooBar" snakeCase("fooBar"); // "foo_bar" kebabCase("FooBar"); // "foo-bar" pascalCase("foo-bar"); // "FooBar" constantCase("fooBar"); // "FOO_BAR" capitalize("hello"); // "Hello" deburr("cafe\u0301"); // "cafe" escapeRegExp("a.b*c"); // "a\\.b\\*c" ``` ### Math ```typescript import { sum, sumBy, mean, meanBy, round, clamp, inRange, random, randomInt, } from "es-toolkit/math"; sum([1, 2, 3]); // 6 sumBy(products, p => p.price * p.quantity); // total round(1.2345, 2); // 1.23 clamp(15, 0, 10); // 10 inRange(5, 1, 10); // true randomInt(1, 100); // random integer in [1, 100) ``` ### Predicate (Type Guards) ```typescript import { isNil, isNotNil, isEqual, isEmpty, isPlainObject } from "es-toolkit/predicate"; isNil(null); // true — null or undefined isNotNil(value); // true — narrows type to exclude null/undefined isEqual({ a: 1 }, { a: 1 }); // true — deep structural equality isEmpty([]); // true isPlainObject({}); // true ``` ### Util ```typescript import { attempt, attemptAsync, invariant } from "es-toolkit/util"; // Safe sync execution — returns [error, result] tuple const [error, result] = attempt(() => JSON.parse(input)); // Safe async execution const [error, data] = await attemptAsync(async () => { const res = await fetch("/api/data"); return res.json(); }); // Invariant — throws if condition is falsy invariant(user != null, "User must exist"); ``` ## Lodash Migration For complete lodash migration details, see [references/lodash-migration.md](references/lodash-migration.md). Key points: - `es-toolkit/compat` provides 100% lodash test compatibility (v1.39.3+) - Import from `es-toolkit/compat` for drop-in replacement - Prefer native `es-toolkit` over `es-toolkit/compat` for better performance - NOT supported: `sortedUniq`, `sortedUniqBy`, `mixin`, `noConflict`, `runInContext`, method chaining (Seq) ## Full API Catalog For the complete list of all functions by category, see [references/api-catalog.md](references/api-catalog.md).
View on GitHub