Performance

What NibCSS costs, how to make static markup free, and how to keep dynamic apps bounded.

1 min readv1

The cost model#

Cost

Each distinct rule

one CSS rule, emitted once for the whole app

Static attributes with the plugin

a normal stylesheet; no JavaScript if rewrite: true

Runtime (CDN / adapters)

~23 kB script, one <style>, a MutationObserver

A dynamic value

one rule per distinct value

Because rules are deduplicated, stylesheet size grows with the number of different rules you write, not with page count. A typical app stays in the low tens of kilobytes.

Make static markup free#

  1. Use the build plugin so static attributes land in a .css file.
  2. Add rewrite: true — those elements need no runtime at all.
  3. Load the runtime only if you have dynamic values (or skip it entirely).

Keep dynamic values bounded#

  • Prefer recipes for small sets of states (badge-green, badge-red) — compiled at build time.
  • Keep interpolated parts small: static rules in nib, only the changing rule in useNib.
  • Use CSS variables for continuous values (drag, animation, charts).
  • The runtime garbage-collects unused rules by default (every ~60 flushes or above 4,000 rules):
TypeScript
init({ gc: { everyFlushes: 30, maxRules: 2000 } }); // tune
init({ gc: false });                                 // manual: runtime.gc()

Measure#

  • runtime.engine.stats() → { rules, tokens }.
  • DevTools → Coverage shows how much of the stylesheet a page uses.
  • Watch rules over a long session — if it only grows, a value is being interpolated too freely.

Next steps#