plugins/frontend-toolkit/skills/bundle-optimization/SKILL.md
Reduce initial JS bundle size via Bundle Analyzer, optimizePackageImports, code splitting, and unused-dep removal. Use when bundle exceeds target, after adding a large dependency, when Lighthouse Performance drops, or before shipping. Not for diagnosing runtime Core Web Vitals (use rendering-performance) or evaluating a library's size before adopting it (use new-tech-evaluation).
npx skillsauth add jaykim88/claude-ai-engineering bundle-optimizationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Reduce initial JavaScript payload so First Contentful Paint and Time to Interactive stay fast. Each kilobyte less = lower mobile failure rate, lower battery use, faster page.
Universal — bundle-bloat patterns (barrel imports, dead code, duplicate-purpose deps, unnecessary client work) are bundler-agnostic; only the analyzer tool and config syntax differ.
Analyze the bundle — per route, not just total
Audit barrel-file imports (#1 silent bundle bloat)
index.ts re-exports (icon sets, date-fns, lodash, @radix-ui/*) pull the whole library when tree-shaking can't resolve the re-exportdate-fns/addDays)optimizePackageImports, …)2b. Move heavy workloads to the server + keep the client boundary narrow
architecture-improvement).'use client', Astro client:* islands, Nuxt <ClientOnly>, …)Identify defer candidates
next/dynamic / React.lazy, …)Tree-shaking checks
import * as X from 'lib' with named importsRemove unused dependencies
npx knip — finds unused exports AND unused depsnpx depcheck for deps onlyDetect duplicate dependencies
moment + date-fns, axios + ky + native fetch → standardize on onenpm ls <pkg> / npm dedupe) — transitive version duplication (two React copies, several lodash) is invisible to "one purpose, one lib" yet ships twiceAudit polyfills
target in tsconfig.json and browserslist should reflect actual support matrixVerify (validation loop)
rendering-performance skill| Tier | Examples | Action SLA | |---|---|---| | Critical | Initial JS > 500KB gzip; single dependency > 200KB gzip; duplicate-purpose deps (moment + date-fns + dayjs all installed) | Block release; fix immediately | | Major | Initial JS 200-500KB; ≥ 3 unused dependencies; barrel-file imports for 5+ packages | Fix this sprint | | Minor | Initial JS < 200KB but room to improve; 1-2 unused dependencies; minor tree-shaking gaps | Schedule within 2 sprints |
Default target if no project-specific target is set: ≤ 200KB initial JS per route (compressed-as-served).
size-limit / bundlewatch) so regressions fail the PR (wire via cicd-pipeline)knip clean); no duplicate deps (same-purpose or multiple versions)optimizePackageImports or subpath)knip / depcheck — runtime-loaded deps (e.g., dynamic import() strings) can be missed by static analysisnext/dynamic that lives above the fold — risk of CLS or LCP regressionnext.config.ts with optimizePackageImports (or serverExternalPackages for server-only deps)perf(bundle): switch to subpath imports for <package>docs/bundle-size-YYYY-MM-DD.md with:
npx next experimental-analyze (Turbopack, Next.js 16.1+) or @next/bundle-analyzer (Webpack)optimizePackageImports: ['lucide-react', 'date-fns', ...] in next.config.tsnext/dynamic({ ssr: false }) or React.lazy'use client' boundary: place at the smallest interactive leaf, not layout/page root — everything below it ships to the clientserverExternalPackages: ['sharp', 'puppeteer'] in next.config.ts — excludes from server bundle (loaded at runtime)npx knip or npx depchecknpx vite-bundle-visualizer; build.rollupOptions.output.manualChunks; dynamic import() for code splittingexperimental.payloadExtraction; defineAsyncComponent; nuxt-icon (lazy-loaded SVGs)import(); +page.svelte is naturally lazy per routeng build --stats-json + webpack-bundle-analyzer; loadChildren for lazy routes; standalone components for tree-shakingimport { x } from 'lib' when the lib's index.ts re-exports 100 things); use direct subpath imports; knip works across stacksrendering-performance — bundle size affects LCP/FCParchitecture-improvement — barrel files and feature boundaries influence bundlingnew-tech-evaluation — evaluate library size before adoptingindex.ts re-exports) defeat tree-shaking and silently bloat bundles — use optimizePackageImports for libraries with hundreds of exports (lucide-react, lodash, date-fns) before reaching for manual next/dynamic. Most "bundle bloat" issues resolve at the import level without changing component code.development
Audit and optimize third-party scripts — analytics, tag managers, chat widgets, embeds — with the right loading strategy, performance budget, facades, and CSP/consent controls. Use when adding a script, when TBT/INP regress, when a GDPR/CCPA consent requirement arises, or before shipping. Not for first-party bundle size (use bundle-optimization) or broad Core Web Vitals diagnosis (use rendering-performance).
development
Apply the Testing Trophy (mostly integration tests with RTL + MSW, sparing E2E with Playwright) and set coverage thresholds. Use before new feature work, after bug fixes, when CI coverage falls below target, or when tests are flaky or break on every refactor. Not for wiring coverage gates + Playwright into the GitHub Actions matrix (use cicd-pipeline) or auditing WCAG a11y compliance (use accessibility-audit).
development
Inventory and prioritize technical debt — TODO/FIXME/HACK, any usage, deprecated APIs, untested logic — with impact × effort matrix. Use at quarter start, before a refactoring sprint, when a new teammate joins, or when feature velocity slows. Not for actually paying down debt (use code-refactoring) or recording a migration approach (use decision-records) — this only inventories and prioritizes.
development
Decision framework for choosing the right state location — URL, server cache, local component, or shared/global store. Use when state-sync bugs appear, prop drilling gets deep (3+ levels), filters/tabs lose state on reload, or quarterly review. Not for form state specifically (use form-ux) or when the state is actually server data (use api-caching-optimization).