Actionable ruleset for keeping JavaScript/TypeScript web-application bundles lean through code-splitting, tree-shaking, and continuous size monitoring.
Your users are abandoning your app before it loads. Every unnecessary kilobyte costs conversions, impacts Core Web Vitals, and burns mobile data. While other developers ship monolithic bundles and wonder why their Lighthouse scores tank, you're about to implement a bulletproof system that keeps your JavaScript lean and your users engaged.
Modern web applications are drowning in JavaScript bloat. The median bundle size has grown 300% in five years, yet loading performance expectations have only increased. You're fighting three critical battles:
The Initial Load Problem: Users bounce after 3 seconds, but your bundle takes 8 seconds to parse on mid-tier devices. Code splitting isn't optional anymore—it's survival.
The Dependency Creep Problem: That innocent npm install just added 47 transitive dependencies and 180kB to your bundle. You need automated size monitoring and lightweight alternatives.
The Production Optimization Gap: Your development build works great, but production bundles are 40% larger than they should be because tree-shaking isn't configured properly and polyfills ship to modern browsers.
These Cursor Rules implement advanced bundling strategies used by high-performance teams at scale. Instead of reactive optimization, you get proactive size management with automated enforcement and intelligent splitting patterns.
Intelligent Code Splitting: Automatic route-level and component-level splitting with strategic chunk grouping. Your users load only what they need, when they need it.
Aggressive Tree Shaking: Proper ES module configuration, side-effect marking, and import optimization that eliminates dead code before it reaches your bundle.
Continuous Size Monitoring: CI integration that fails builds when bundles exceed thresholds and provides actionable remediation steps.
60% Faster Initial Load: Route-based splitting reduces initial bundle from 400kB to 120kB, with remaining features loading on-demand.
Automated Size Regression Prevention: CI checks fail when bundle diff exceeds 3kB, preventing gradual bloat that kills performance over time.
Modern Browser Optimization: Smart polyfill loading and native ES module usage that eliminates unnecessary transforms for 95% of your users.
Framework-Optimized Patterns: React.lazy, Next.js dynamic imports, and Angular lazy loading configured for maximum efficiency.
// Importing entire libraries
import _ from 'lodash';
import moment from 'moment';
// No size awareness
const HeavyDashboard = () => {
return <ExpensiveChart data={processData()} />;
};
// Build passes, users suffer
npm run build // ✅ Build successful (1.2MB bundle)
// Tree-shakeable imports
import { debounce } from 'lodash-es';
import { format } from 'date-fns';
// Lazy-loaded components
const HeavyDashboard = React.lazy(() =>
import('./HeavyDashboard' /* webpackChunkName: "dashboard" */)
);
// Automated size enforcement
npm run build // ❌ Failed: Bundle exceeds 200kB limit
// 💡 Suggestion: Split vendor chunk, review large dependencies
// Vendor chunk optimization
export default {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
chunks: 'initial',
name: 'vendor',
enforce: true
},
react: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'react',
chunks: 'all'
}
}
}
}
};
# Install size monitoring tools
npm install --save-dev size-limit @size-limit/preset-web-app
npm install --save-dev webpack-bundle-analyzer
# Add to package.json
{
"scripts": {
"analyze": "webpack-bundle-analyzer build/static/js/*.js",
"size": "size-limit"
},
"size-limit": [
{
"path": "build/static/js/*.js",
"limit": "200 kB"
}
]
}
// webpack.config.js
module.exports = {
mode: 'production',
optimization: {
concatenateModules: true,
sideEffects: false,
splitChunks: {
chunks: 'all',
minSize: 25 * 1024,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'initial',
enforce: true
}
}
}
},
plugins: [
new CompressionPlugin({
algorithm: 'brotliCompress',
compressionOptions: { level: 11 }
})
]
};
// ❌ Avoid: imports entire library
import _ from 'lodash';
// ✅ Use: tree-shakeable named imports
import { debounce, throttle } from 'lodash-es';
// ✅ Dynamic imports for heavy components
const AdminPanel = React.lazy(() =>
import('./AdminPanel' /* webpackChunkName: "admin" */)
);
// ✅ Conditional polyfill loading
if (!window.IntersectionObserver) {
import('intersection-observer');
}
# .github/workflows/bundle-size.yml
name: Bundle Size Check
on: [pull_request]
jobs:
size-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: npm ci
- name: Build production
run: npm run build
- name: Check bundle size
run: npx size-limit
- name: Comment bundle analysis
uses: github/super-linter@v4
with:
files: build/static/js/*.js
Week 1: 45% reduction in initial bundle size through proper import optimization and basic code splitting.
Month 1: Automated CI checks prevent 12 potential size regressions. Average bundle diff per PR drops from +15kB to +1.2kB.
Quarter 1: Lighthouse Performance scores improve from 65 to 92. Time to Interactive decreases by 2.3 seconds on mobile devices.
Advanced Results: Teams report 25% fewer performance-related user complaints and 18% improvement in conversion rates on slower connections.
Your bundle optimization system runs continuously, catching size issues before they impact users and providing clear guidance for maintaining lean, fast applications. Every byte counts, and now you're counting them automatically.
You are an expert in ES2015+, TypeScript, Webpack 5, Rollup, Vite, Babel, Terser, React 18, Next.js 14, Angular 17, Lighthouse, and modern CI/CD tooling.
Key Principles
- Treat every byte as a cost. Establish and enforce bundle‐size budgets from day one.
- Favour **code splitting, lazy loading, and tree shaking** over monolithic bundles.
- Ship only production-ready code: always build with NODE_ENV=production and --mode production.
- Prefer native browser capabilities (ES Modules, WebP/AVIF, Brotli) before adding polyfills or heavy libraries.
- Continually measure: "you can’t optimise what you don’t measure". Automate size diff checks in CI.
JavaScript / TypeScript Rules
- Use **ES Modules exclusively** (import/export). CommonJS is forbidden in client bundles.
- Always use **named imports**: `import { map } from 'lodash-es'` not `import _ from 'lodash'`.
- Avoid wildcard `import * as` statements—import at function level when possible.
- Mark side-effect-free packages: add `"sideEffects": false` in package.json or per-file comments `/*#__PURE__*/`.
- Prefer lightweight, tree-shake-friendly alternatives (e.g. `date-fns` over `moment`, `lodash-es` over `lodash`).
- Inline small utility functions (< 3 LOC) instead of installing micro-libraries.
- Use **dynamic `import()`** for route- or feature-level lazy loading; group related code with webpack’s `webpackChunkName` magic comments.
- Keep polyfills behind feature detection + dynamic import to avoid shipping to modern browsers.
Error Handling & Validation
- Build scripts must fail if any bundle (parsed) > 200 kB or (gzipped) > 70 kB. Configure Webpack `performance.maxAssetSize` & `maxEntrypointSize`.
- CI job `check-bundle-size` compares current build to `main`; fail if diff > 3 kB. Use `size-limit` or `bundlesize`.
- Warn during build when files are created in `node_modules` without ESM entrypoints.
- Log clear remediation steps: list offending modules, suggest lighter alternatives, or splitting points.
Framework-Specific Rules
React
- Use `React.lazy(() => import('./HeavyPage'))` with `<Suspense fallback={...}>` for route & component splits.
- Replace large icon packs with tree-shakeable variants (e.g. `@heroicons/react/24/solid/ClockIcon`).
- Memoise and export only memoised versions of frequently re-rendered components to avoid code duplication in multiple chunks.
Next.js
- Use `next/dynamic` for client-side only code; set `ssr: false` to prevent server bundle bloat.
- Enable `@next/bundle-analyzer` locally and in CI (`ANALYZE=true next build`).
- Leverage built-in Image component with AVIF/WebP and `priority` attribute for LCP images only.
Angular
- Structure modules for **route-level lazy loading**: `loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)`.
- Set `optimization: true`, `buildOptimizer: true`, `namedChunks: false` in angular.json production config.
Tooling & Build
- Webpack 5: enable `concatenateModules`, `sideEffects: true`, `splitChunks: { chunks: 'all', minSize: 25*1024 }`.
- Add `TerserPlugin` with settings `{compress: {drop_console: true, pure_funcs: ['console.info','console.debug'] }, mangle: true}`.
- Use `CompressionPlugin` to pre-generate Brotli (`algorithm: 'brotliCompress', compressionOptions: {level: 11}`).
- Rollup: configure `treeshake: {moduleSideEffects: false}`, use `@rollup/plugin-terser`, `rollup-plugin-visualizer` post-build.
- Vite: `build.minify='terser'`, `build.rollupOptions.output.manualChunks` for vendor splitting.
Testing & CI
- Add `yarn size-limit` with config per entry (`path: './src/index.tsx', limit: '65 kB'`).
- Provide GitHub Action that runs `npm run analyze` (webpack-bundle-analyzer + `--json stats.json`) and comments diff on PR.
- Use Lighthouse CI in `mobile` mode; require performance score ≥ 90; gating PRs on regressions.
Performance Patterns
- Split vendor chunk further: isolate state-management libs (e.g. redux) to enable long-term browser caching.
- Inline critical CSS (< 5 kB) into HTML; lazy load the rest via `loadCSS` pattern.
- Defer third-party scripts with `async`/`defer` and use `partytown` or `webworkers` for heavy analytics.
Security Notes
- Never include `.map` files in production; expose them only behind auth for error tracking tools.
- Validate external dynamic imports; whitelist routes and sanitise variables to avoid injection attacks.
Common Pitfalls & Fixes
- "Module not included in build": ensure package exports ESM; otherwise transpile with `babel-loader` + `@babel/preset-env` and `modules: false`.
- "Large polyfill chunk": drop core-js stages you don’t need; target modern browsers via `.browserslistrc`.
- "Vendor duplication": enable `dedupe` in Vite/Rollup and review module versions across workspace.
Directory & File Naming
- Keep `pages/*` or `routes/*` as top-level route bundles.
- Place shared, truly reused utilities under `shared/` to avoid multiple copies after code splitting.
- Chunks are automatically kebab-cased; override with webpack `/* webpackChunkName: "admin-dashboard" */` when debugging.
Checklist Before Merge
1. `npm run build:prod` succeeds with performance budgets.
2. `npm run analyze` shows no single chunk > 150 kB (parsed).
3. Lighthouse mobile score ≥ 90.
4. New dependencies are reviewed for ESM support & bundle impact.
5. CI `check-bundle-size` passes (< +3 kB diff).