Performance budgets are critical for keeping your site fast. Learn how to automate regression testing using Lighthouse CI and GitHub Actions to catch bloat.
We’ve all been there: a minor feature request lands, the PR gets merged, and suddenly your LCP jumps by 400ms. It’s the classic "death by a thousand cuts" scenario, where individual commits seem harmless until the aggregate weight crushes your site's performance.
I used to rely on manual audits, but that’s a fool’s errand. If you aren't automating your checks, you’re just hoping for the best. To actually maintain a fast site, you need to treat performance as a first-class citizen in your CI/CD pipeline.
A performance budget is your line in the sand. It’s a set of constraints—like bundle size, image weight, or Core Web Vitals—that your application cannot exceed. When you define these, you move from "we should be faster" to "this PR cannot be merged because it exceeds our 250kb JS budget."
Setting these up isn't just about speed; it's about team culture. When developers see a red checkmark in GitHub because they added a heavy library, they learn to optimize early. It beats refactoring a bloated codebase three months later.
If you're already optimizing your main application, you might want to look into how Performance budgets: Enforcing Bundle Size and Vital Thresholds provide the foundational logic for these constraints.
Lighthouse CI is the gold standard for this. It runs the same audits you see in Chrome DevTools but does so in an isolated, repeatable environment.
First, install the CLI:
Bashnpm install -g @lhci/cli
You’ll need a lighthouserc.js file in your root. This is where you define your thresholds. Don't go overboard; start with what you can actually control, like Total Blocking Time (TBT) or specific asset sizes.
JAVASCRIPTmodule.exports = { ci: { assert: { preset: CE9178">'lighthouse:recommended', assertions: { CE9178">'categories:performance': [CE9178">'error', {minScore: 0.9}], CE9178">'total-byte-weight': [CE9178">'error', {maxNumericValue: 500000}], }, }, }, };
This is where the magic happens. By adding a workflow file in .github/workflows/lighthouse.yml, you trigger these audits on every pull request. We’ve found that running this on a staging build is the most reliable way to get accurate numbers.
YAMLjobs: lhci: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - run: npm install && npm run build - name: Run Lighthouse CI uses: treosh/lighthouse-ci-action@v9 with: configPath: './lighthouserc.js'
When this runs, the action will comment on your PR with a summary of the audit. If a number dips below your budget, the build fails. It’s blunt, effective, and keeps everyone honest.
We once tried setting an aggressive 100ms budget for INP (Interaction to Next Paint) across all devices. It failed constantly because our staging environment’s network throttling was inconsistent. We had to dial it back and focus on the "p95" performance metrics rather than absolute perfection.
Also, don't forget that performance isn't just about the initial load. If you're building complex data-heavy apps, you might need to handle intense server-side logic effectively, which is why I often suggest checking out Next.js Server Actions: Implementing Synthetic Monitoring and Backpressure to ensure your backend isn't bottlenecking your frontend speed.
Should I block merges for every minor regression? No. Start with "warnings" in your CI logs. Once the team gets used to the metrics, flip the switch to "error" to block merges. If you start by breaking everyone's workflow, they'll just disable the tool.
How do I handle third-party scripts? Third-party code is the biggest performance killer. If a marketing script bloats your bundle, you need a process to audit its necessity. Sometimes, you have to exclude these from your strict budgets, but be honest about why.
What happens if the budget is met, but the site still feels slow? Budgets are guardrails, not a total solution. They won't catch bad UX patterns or slow API response times. Use them in tandem with Real User Monitoring (RUM) to get the full picture.
Automating your performance budgets is a journey. You’ll start with a few basic checks, realize your infrastructure is noisy, and eventually build a robust system that catches regressions before they hit your users.
Don't treat this as a "set it and forget it" task. Revisit your budgets every quarter. As your app grows, your constraints should evolve. What I’m still figuring out is how to better handle flaky tests in CI—sometimes the network jitter ruins a perfectly good build, and that’s a frustration I haven't fully solved yet.
Master browser caching and network congestion management. Learn how to use HTTP/3 and smarter cache headers to stop resource contention and boost speed.
Read moreMaster data hydration using stale-while-revalidate and Server-Sent Events to drastically improve LCP and INP for a snappier, more responsive user experience.