fastdom

Batches DOM measure and mutate calls into requestAnimationFrame queues to eliminate layout thrashing in web apps.

Library
npm
v1.0.12
6,914stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
46/100Fair
Development Activity0
Maintenance20
Community64
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
70/100Good
Architecture85
Code Quality62
Innovation78
Learning Curve55

FastDom is a tiny (about 600 bytes minified and gzipped) utility that eliminates layout thrashing by batching DOM measurement and mutation work into requestAnimationFrame-driven queues. Instead of every module in an app reading and writing to the DOM whenever it feels like it — forcing the browser into repeated synchronous reflows — code calls fastdom.measure() for reads and fastdom.mutate() for writes, and FastDom guarantees all queued reads run before all queued writes on the next frame.

FastDom is designed as an app-wide singleton: whichever module calls require('fastdom') gets the same instance back, so unrelated libraries and app code all schedule against one shared queue. An extend() API lets consumers layer extensions like fastdom-promised (Promise-based measure/mutate) or fastdom-sandbox (grouped/scoped tasks) on top without breaking that singleton guarantee, and a companion fastdom-strict script can enforce the discipline in development by throwing when DOM properties are touched outside their designated phase.

What You Get

  • A singleton fastdom instance with measure()/mutate() methods that queue DOM reads and writes separately
  • Automatic requestAnimationFrame-based batching so all queued reads run before all queued writes each frame
  • A clear() method to cancel a previously scheduled measure or mutate task
  • An extend() API for composing extensions (a Promise-based API, task grouping) onto the shared singleton without creating a second instance

Common Use Cases

  • Batching layout reads (offsetWidth, getBoundingClientRect) and style writes across a large DOM tree to avoid forced synchronous reflow
  • Coordinating DOM updates across multiple unrelated modules or third-party widgets that would otherwise thrash layout independently
  • Debugging layout thrashing during development using the fastdom-strict companion, which throws on out-of-phase DOM access
  • Wrapping fastdom in Promises via fastdom-promised for async/await-friendly measure/mutate chains

Under The Hood

Architecture FastDom is a tiny singleton module (fastdom.js, ~5KB) built around a classic prototype-based constructor: a FastDom instance holds two arrays (reads, writes) and a scheduleFlush/flush pair driven by requestAnimationFrame; measure() and mutate() push bound tasks onto their respective queues and lazily schedule a single rAF callback per frame via a scheduled flag, and flush() drains reads then writes inside a try/catch so a thrown task doesn’t stop the batch (errors are re-queued and re-flushed, or routed to a user-supplied catch hook). The genuinely interesting piece is extend(), which uses Object.create(this) plus a mixin helper to let extensions (fastdom-promised, fastdom-sandbox) layer new behavior onto the same singleton instance via prototypal inheritance while still funneling every scheduled task through the same global reads/writes arrays — so the core abstraction that would break everything if changed is the singleton-plus-two-queues design living on win.fastdom.

Tech Stack Vanilla ES5 JavaScript with zero runtime dependencies beyond an optional strictdom devDependency; built as a UMD-style IIFE exposing itself via CommonJS module.exports, AMD define(), or a global window.fastdom. Build tooling is Webpack 1.x plus uglify-js for the minified distributable; tests run under Karma against real/headless browsers using Mocha, Chai, and Sinon, with Travis CI as the historical CI target and Coveralls for coverage reporting. A hand-written TypeScript declaration file ships alongside the plain-JS source for typed consumers, and a bower.json reflects the library’s origins in the front-end-package-manager era.

Code Quality The core test suite (nearly 400 lines) exercises ordering guarantees (reads always before writes), re-entrant scheduling from within a read/write callback, clear() cancellation, and error-recovery/catch hook behavior, with separate suites covering the fastdom-promised, fastdom-sandbox, and fastdom-strict extensions using Sinon spies and async done()-style assertions. Linting is jshint rather than ESLint, enforcing single quotes and an 80-column limit; there are no TypeScript types on the implementation itself, only a hand-authored .d.ts for consumers, so type safety is asserted rather than compiler-checked. Naming is consistent and every public method carries a JSDoc-style comment block, though the Travis CI badge points at a now-defunct pipeline, leaving current CI health unverifiable from the repo alone.

API Design The public surface is minimal by design — measure(), mutate(), clear(), and extend() — mirroring requestAnimationFrame’s own read/write phase separation so the mental model transfers almost for free from browser rendering internals. extend() is the standout DX choice: rather than forcing users to pick between a callback version or a Promise version of the library up front, it lets them compose exactly the extensions they want onto one shared singleton, so third-party code and app code never end up scheduling against two different instances. Getting started requires nothing beyond require('fastdom') — no configuration or setup — and the README documents the full API with runnable snippets and an explicit “how it works” section, though it stops short of an interactive docs site.

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