Frontend Performance Budgets for SaaS That Actually Stick
How to set and enforce frontend performance budgets in a SaaS product—Core Web Vitals targets, bundle limits, CI gates, and the metrics that matter.
Performance is a feature, but "let's be faster" is not a plan. In a SaaS product where dashboards grow every sprint, speed erodes one innocent dependency at a time. The fix isn't heroics—it's a budget you enforce in CI so regressions get caught before they ship. Here's how I set them.
Budget the metrics users feel
Start with Core Web Vitals, because Google ranks on them and users feel them:
- LCP (Largest Contentful Paint) — under 2.5s. The main content, not a spinner.
- INP (Interaction to Next Paint) — under 200ms. This replaced FID and is the real measure of "does the app feel snappy?"
- CLS (Cumulative Layout Shift) — under 0.1. Reserve space for images and async content.
These are field targets. Measure them with real-user monitoring (RUM), not just a lab run on your fast laptop.
Budget the bytes that cause them
Vitals are outcomes; bytes are the input you control. Set hard limits on the initial payload:
- JavaScript (initial route): ~170kb gzipped is a sane ceiling for an interactive app.
- CSS: keep it lean—Tailwind's purge should leave you well under 30kb.
- Images: serve modern formats (AVIF/WebP), size them for the layout, and lazy-load below the fold.
Enforce it in CI, or it won't happen
A budget nobody checks is a wish. Wire it into the pipeline so a regression fails the build. Two layers work well.
Bundle size gate with size-limit:
{
"size-limit": [
{ "path": ".next/static/chunks/main-*.js", "limit": "170 kB" }
]
}
Lighthouse CI for the vitals, asserting on the numbers:
// lighthouserc.js
module.exports = {
ci: {
assert: {
assertions: {
"largest-contentful-paint": ["error", { maxNumericValue: 2500 }],
"cumulative-layout-shift": ["error", { maxNumericValue: 0.1 }],
},
},
},
};
Now "it got slower" is a red check, not a vibe someone raises three releases later.
The habits that keep budgets green
- Code-split by route and lazy-load heavy, rarely-used surfaces (charts, editors, modals).
- Ship less JavaScript. Prefer Server Components and server-side data fetching where the framework allows.
- Audit dependencies before adding them. A date library can cost more than the feature it enables—check the gzipped size first.
- Defer the non-critical. Analytics, chat widgets, and feature flags should never block first paint.
- Watch the p75, not the average. Averages hide your slowest users; the 75th percentile is what Google reports and what real people experience.
Make it cultural
The tooling is the easy 20%. The other 80% is treating a failed budget like a failed test—something you fix, not override. Put the numbers in the PR template. Celebrate when someone deletes a dependency. Once the team internalizes that fast is the baseline and slow needs justification, the budget stops being a gate and becomes how you build.
Fast products feel trustworthy. In SaaS, that trust compounds into retention—which is why performance is never just an engineering concern.
About Ansh
Frontend engineer with 4+ years building scalable SaaS products, design systems, CRM, analytics and omnichannel platforms.
More about me →