Frontend Utility Library

Status: Draft

Assignee: Maiia Kuzmishyna

Problem

The codebase contains several hand-written code blocks that duplicate what a utility library could achieve in a well-tested and readable form.

Examples:

  • Deep-copying objects is done via JSON.parse(JSON.stringify(...)) (deepCopyGeometry in PolygonUtils.ts).
  • Object filtering is implemented with a three-step Object.keys + filter + reduce pipeline (filterObject.ts).
  • Finding minimum value in a collection is handled by two separate reduce calls with Math.min, (copyDrawingsForPlacing in drawing-utils.ts).

Moreover, there are a couple of future use cases that could be covered by a utility library.

  • Deep-equals can be very useful for memoization - it allows to completely forgo memoization of object props if a deep equality function is supplied as a second argument to React.memo().
  • Debounce and throttle can be crucial for performance optimizations as they allow reducing the number of times an expensive computation executes. Debouncing and throttling are also known for being notoriously difficult to implement from scratch without mistakes.

Last but not least, there is one use case already covered by a specific utility library lodash which should also be considered when choosing a library.

  • Range construction from zero to a given number in BezierPolygon.tsx.

Constraints

  1. The library must be well-maintained, widely adopted, and have a stable release history.
  2. The library must cover all use cases listed in the Problem section: deep clone, deep equals, object omit, minBy, flatMap, debounce, throttle, and range.
  3. The library must be licensed under a permissive open-source license (e.g. MIT) to allow free use in this project.
  4. The library should support usage in typescript projects.
  5. Adding the library should not significantly increase bundle size.

Assumptions

  1. Implementing and maintaining utility functions from scratch requires disproportionate effort and introduces avoidable risk compared to adopting a well-tested library.
  2. Tree-shaking is effective in the Vite-based build pipeline, so unused library functions are excluded from the production bundle.
  3. The transitive presence of lodash in the dependency graph does not guarantee its API stability or continued availability; an explicit dependency should be declared for any library actively used in production code.

Solutions

Alternative A: lodash (by John-David Dalton)

GitHub

Lodash is the most widely used JavaScript utility library, with over 60 million weekly npm downloads and a long release history dating back to 2012.

Declaring it as an explicit dependency therefore does not increase bundle size, and the import style import { range } from 'lodash-es' (lodash's ES module build) enables full tree-shaking via Vite.

Constraint fulfillment:

  • Maintenance & adoption: actively maintained, 10k+ issues resolved, backed by the OpenJS Foundation.
  • Use case coverage: _.cloneDeep, _.isEqual, _.omit, _.minBy, _.flatMap, _.debounce, _.throttle, _.range are all available.
  • License: MIT.
  • Typescript: TypeScript types are provided by the @types/lodash package.
  • Bundle size: 80.6kB minified. The frontend already uses it (in BezierPolygon.tsx) without declaring it as an explicit dependency — it is pulled in transitively via @storybook/addon-controls and vite-plugin-pwa. Declaring it as an explicit dependency therefore should not increase bundle size. Moreover, removing the usage in BezierPolygon.tsx will not reduce bundle size either. Alternatively, a tree-shakable build of lodash could be imported (lodash-es).

Alternative B: es-toolkit (by Toss)

GitHub

es-toolkit is a modern utility library designed as a drop-in alternative to lodash, built natively in TypeScript with tree-shaking as a first-class concern. It covers all the required use cases: cloneDeep, isEqual, omit, minBy, flatMap, debounce, throttle, and range. Benchmarks published by the authors show 2–3× smaller bundle sizes compared to lodash-es for equivalent imports, owing to leaner implementations without legacy browser support code.

Constraint fulfillment:

  • Maintenance & adoption: actively maintained since 2024, growing adoption; backed by Toss (a large fintech company).
  • Use case coverage: all required functions are available.
  • License: MIT.
  • Typescript: supported out-of-the-box.
  • Bundle size: 28.3kB minified, supports tree-shaking.

es-toolkit is relatively new and has a smaller community and ecosystem.

Alternative C: Radash (by Ray Oelzer)

GitHub

Radash is a TypeScript-first utility library with a focus on modern, readable functional patterns. It covers most of the required use cases: clone (shallow), omit, min, flat, and a rich set of async utilities. However, debounce and throttle are not part of Radash's API, and isEqual (deep equality) is absent as well, which means use cases constraint would not be met without supplementing Radash with another library.

Constraint fulfillment:

  • Maintenance & adoption: actively maintained, however a smaller community than lodash or es-toolkit.
  • Use case coverage - NOT FULFILLED: debounce, throttle, and deep equality are missing.
  • License: MIT.
  • Typescript: supported out-of-the-box.
  • Bundle size: 11.8kB minified, supports tree-shaking.

Decision

Rationale

Implications

Notes