Performance

Granularity is the product's main promise. This page shows what it is measured in and how to verify it yourself.

Machine-translated, not yet reviewed. Read the original

The promise is short: the bundle grows with what you use, not with what the package contains. Below is what backs it, what it actually costs and how to measure it at home without taking anyone’s word.

Two sides, and they do not correlate

A component’s weight is code plus styles, and the two live apart:

ComponentCSS added to the sheetJS, the component itself
GrCard223 B1 870 B
GrDialog318 B9 789 B
GrDataTable705 B16 243 B
GrSelect2 335 B19 819 B

A component heavy in code may add almost nothing to the sheet, and the other way round. So “the weight of a component” is not a single number, and any table with one column is lying.

Code: a subpath instead of a barrel

ts
// one component and its graph
import { GrButton } from '@feugene/granularity/components/GrButton'

// the whole package
import { GrButton } from '@feugene/granularity'

The top line is granularity itself. The spread between the lightest and the heaviest subpath is nearly two orders of magnitude: GrButtonGroup pulls 1.8 kB gzipped, under one per cent of the barrel, and GrDialogService pulls 89.3 kB. The whole package from the root is 537.9 kB.

Subpath weights must not be added up. The shared chunk is counted afresh in every row but paid for once: the sum of the five heaviest rows is 382.3 kB, while together they weigh 171.7 kB. A set is measured as a union rather than a sum — and that is an upper bound: the application’s bundler shakes the tree further and minifies again.

The full table for every subpath is generated from the built dist on every release — it ships inside the package and appears on each component’s page in the catalogue.

Styles: you pay for what you selected

CSS is not imported as files — the preset generates it for exactly the components you listed in components. The spending breaks down like this:

  1. The first component brings the foundation — the tokens, the base layer, the preflight, the theme. That is a one-off.
  2. Each one after it adds its own few hundred bytes — from two hundred for a card to a couple of kilobytes for a select.
  3. The second theme costs about a kilobyte, not double: themes differ in role values, not in rules.

Not listing components at all is a working option: every component of the provider then reaches the sheet. The difference between “everything” and “one” on the shared sheet is roughly twofold rather than a hundredfold, because the foundation is shared. Granular styles are worth having, but the effect is more modest than it is for code, and saying so honestly is cheaper than explaining it later.

Runtime

  • Zero runtime dependencies in the core. The only mandatory external library is @floating-ui/dom, and it is declared as a peer dependency of the application so that it never arrives as a second copy.
  • A component’s CSS travels in its own chunk. There is no separate style import — and that is not a convenience but a condition: otherwise granularity would break on the first forgotten import.
  • Highlighting, the token reference and the test helpers are separate subpaths and never reach the main bundle at all.

How to measure it at home

  1. Build the application with one component and measure the gzip of the emitted CSS and JS.
  2. Add a second component and measure again. The difference is its real cost in your project.
  3. Compare against the barrel: replace the subpath with a root import and repeat. The difference is what you pay for the convenience.

The numbers on this site were taken exactly that way. Treat them as an order of magnitude: your result depends on the bundler version, the minifier settings and on what else is in the graph.

What will speed nothing up

  • importStyle: true on the auto-import resolver. The core does not need it: most components have no CSS of their own, and the rest carry it in their chunk.
  • Manual styles/*.css imports on top of the preset. They add weight rather than removing it: the preset has already generated the same thing.
  • Narrowing themes.names to one theme to save space. The second theme costs about a kilobyte — this is not the place to give up theme switching.

Last reviewed: 2026-09-01