The 180KB regression that prompted this setup came from a single dependency version bump, a patch release that pulled in a new transitive dependency neither the PR author nor the reviewer noticed in a diff full of otherwise-unrelated package.json changes. A human review missed it because bundle size isn't something a diff makes visible. A CI check would have.
TL;DR:
size-limitruns in CI and fails a pull request when a bundle's gzipped size exceeds a configured budget, catching regressions at review time instead of after they ship. Set the budget from your current measured size plus 10-20% headroom, not an arbitrary number, and check gzip size by default since that's what's actually transferred to users.
Setting Up size-limit
npm install --save-dev size-limit @size-limit/preset-app
// package.json
{
"size-limit": [
{
"name": "Main bundle",
"path": "dist/assets/index-*.js",
"limit": "180 KB"
},
{
"name": "Vendor bundle",
"path": "dist/assets/vendor-*.js",
"limit": "120 KB"
}
],
"scripts": {
"size": "size-limit"
}
}
Each entry names a specific output chunk and its budget. Splitting the budget by chunk (main app code versus vendor/dependency code) rather than one combined number makes a regression easier to diagnose from the CI failure alone, "vendor bundle exceeded its limit" points straight at a dependency change, where a single combined budget would just say "something got bigger" without narrowing down where.
Wiring It Into GitHub Actions
# .github/workflows/size-check.yml
name: Bundle Size Check
on:
pull_request:
branches: [main]
jobs:
size:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm run build
- name: Check bundle size
run: npx size-limit
npx size-limit exits with a non-zero code if any chunk exceeds its budget, which fails the CI job and blocks the PR from being merged (assuming this check is set as required in the repo's branch protection rules). The failure message names the specific chunk and by how much it exceeded the limit, giving the PR author a concrete number to investigate instead of a vague "the bundle got bigger" complaint days later.
Posting the Size Diff as a PR Comment
The size-limit GitHub Action posts a comparison comment directly on the PR, showing the size delta against the base branch, which is more actionable during review than a pass/fail check alone:
# Add this step to the same size-check.yml workflow
- uses: andresz1/size-limit-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
This surfaces a table like Main bundle: 174.2 KB (+3.1 KB, +1.8%) directly in the PR conversation, so a reviewer sees the size impact of the change without needing to run anything locally or dig through a separate CI log.
Choosing the Budget Number
npx size-limit
# Main bundle: 162.4 KB / 180 KB (90%)
Run this against your current main branch first to get a real baseline, then set the budget at roughly 110-120% of that measured number, not a round figure picked without reference to the actual codebase. A budget set with no headroom fails on routine, legitimate growth (a new feature that genuinely needs a few more KB) and trains the team to bump the limit reflexively rather than treating a failure as a signal worth investigating. A budget with too much headroom, on the other hand, catches nothing until the bundle has already grown far past where anyone would want it.
What size-limit Doesn't Catch
A per-chunk budget catches growth in the chunks you've configured, it won't catch a new chunk appearing that wasn't in the config at all (a route added without route-level code-splitting, landing entirely inside the main bundle instead of its own chunk), or a regression in a chunk you haven't budgeted. Review the size-limit config itself periodically as the app's chunk structure evolves, an outdated config silently stops protecting the parts of the bundle that changed shape since it was last written.
Old Way vs Modern Way
| Approach | When it catches a regression | Effort to check |
|---|---|---|
| Manual bundle analyzer | Whenever someone remembers to run it | Manual, easy to skip |
| Lighthouse CI performance score | After deploy, aggregated with other metrics | Automated, but not bundle-specific |
| size-limit in CI | At PR review, before merge | Automated, zero ongoing effort once configured |
Conclusion
A bundle size regression is invisible in a normal code review, package.json diffs don't show the transitive dependency tree, and a 180KB jump from a single patch bump reads the same in a diff as a 2KB one. size-limit in CI turns that invisible number into an explicit, enforced check at exactly the point where catching it costs the least, before the PR merges, not three weeks after a user notices.