Optimization loop
biagiojs closes the loop between production traffic and the next build. Field data in reports/ recalibrates component weights and hydration thresholds — no manual graph edits.
deploy → real users → reports/ → biagio build → tuned HTML → deploy
Report files
Place JSON under reports/ (watched in dev):
| File | Source | Effect on build |
|---|---|---|
analytics.json | Your analytics export | interactionProbability ← observed CTR per component |
heatmap.json | Attention / scroll maps | conversionWeight nudged ±0.2 |
searchconsole.json | GSC top landing components | seoWeight +0.15 for matched IDs |
crux.json | CrUX / PageSpeed Insights | Hydration thresholds raised or relaxed |
Example analytics.json:
{
"componentClicks": {
"hero-cta": 0.18,
"newsletter": 0.03
}
}
Example crux.json (from pull-vitals):
{
"p75": { "lcp": 2800, "inp": 220, "cls": 0.05 }
}
Slow field INP/LCP → less eager hydration (higher thresholds). Fast sites hydrate more aggressively.
The build log prints every optimizer decision — inspectable, deterministic, no black box.
biagio pull-vitals
Fetch real-user CrUX percentiles into reports/crux.json:
npx biagio pull-vitals https://yoursite.com/ .
Optional PSI_API_KEY env var for PageSpeed Insights API quota.
Run after deploy (or on a schedule in CI) so the next build reflects how users actually experience the site.
Per-locale reports
For multilingual sites, override per market:
reports/it/analytics.json
reports/de/crux.json
See Internationalization.
A/B experiments
Server-side assignment — zero flicker, zero CLS, no client-side bucketing JS.
export default function ({ ExperimentEngine, userId }) {
const ab = new ExperimentEngine({ userId })
.define('hero_cta', ['control', 'urgency'], { weights: [0.5, 0.5] });
const cta = ab.pick('hero_cta', {
control: () => '<button class="btn">Buy</button>',
urgency: () => '<button class="btn">Buy now — free shipping</button>',
});
// … add cta to graph, optionally ab.beacon() in head for analytics
}
- Same
userId→ same variant (deterministic FNV hash) - Output wrapped in
data-cvw-exp/data-cvw-variantfor measurement ab.beacon()exposeswindow.__CVW_EXPERIMENTS__to your analytics tag
Combine experiments with business weights: winning variants can inform declared weights in the next iteration.
biagio analyze
After build, inspect what shipped:
npx biagio analyze
Writes dist/.biagio-analyze.json — HTML weight per page, island counts, asset totals. When site.optimize.bundleClasses is enabled, the report also shows class bundle aliases and raw HTML bytes saved per route.
Class bundle (opt-in)
For utility-heavy markup (e.g. repeated Tailwind-like class strings), enable:
// biagio.config.js
export default {
site: {
optimize: {
bundleClasses: true,
bundleMinRepeat: 3, // default
bundleMinTokens: 2, // default — skip single-class attrs
},
},
};
At build time biagiojs:
- Finds identical
class="flex items-center …"values that appear ≥bundleMinRepeattimes - Checks that each token has a rule in a page
<style>block (top-level in.biagio, orheadfrom.page.js) - Replaces them with short aliases (
u-0,u-1, …) and injects consolidated CSS - Runs purge afterward so unused utility rules drop out of component styles
Skip an element with data-cvw-no-bundle. Classes inside <template> are never bundled (client-only widgets).
Impact: mostly raw HTML savings; gzip/Brotli already compress repeated strings well. Best on landing pages with many identical utility chains. See demo/pages/bundle-demo.page.biagio.
Pair with Lighthouse on biagio preview for lab vs field comparison.
Related
- CLI — all commands
- Adaptive hydration — how thresholds change behaviour
- Business weights — declared vs learned weights