skillby sgcarstrends
bundle-analysis
Analyze and optimize JavaScript bundle size, identify large dependencies, and improve load times. Use when bundles are large, investigating performance issues, or optimizing frontend assets.
Installs: 0
Used in: 1 repos
Updated: 1d ago
$
npx ai-builder add skill sgcarstrends/bundle-analysisInstalls to .claude/skills/bundle-analysis/
# Bundle Analysis Skill
This skill helps you analyze and optimize JavaScript bundle sizes for Next.js and web applications.
## When to Use This Skill
- Large bundle sizes (>500KB)
- Slow initial page loads
- High First Contentful Paint (FCP)
- Investigating bundle composition
- Identifying duplicate dependencies
- Optimizing production builds
- Reducing Time to Interactive (TTI)
## Bundle Size Goals
- **Initial bundle**: <200KB (gzipped)
- **First Load JS**: <300KB total
- **Route chunks**: <100KB each
- **Vendor chunks**: <150KB
- **Time to Interactive**: <3s on 3G
## Next.js Bundle Analyzer
### Setup
```bash
# Install bundle analyzer
pnpm add -D @next/bundle-analyzer
# Or as dev dependency in web app
cd apps/web
pnpm add -D @next/bundle-analyzer
```
### Configuration
```typescript
// apps/web/next.config.ts
import type { NextConfig } from "next";
import withBundleAnalyzer from "@next/bundle-analyzer";
const bundleAnalyzer = withBundleAnalyzer({
enabled: process.env.ANALYZE === "true",
});
const nextConfig: NextConfig = {
// ... your config
};
export default bundleAnalyzer(nextConfig);
```
### Running Analysis
```bash
# Analyze production bundle
cd apps/web
ANALYZE=true pnpm build
# Opens two HTML reports in browser:
# - client.html: Client-side bundle
# - server.html: Server-side bundle
# Analyze specific environment
ANALYZE=true NODE_ENV=production pnpm build
# Save report to file
ANALYZE=true pnpm build > bundle-report.txt
```
## Reading Bundle Reports
### Bundle Analyzer Output
```
Page Size First Load JS
┌ ○ / 5.2 kB 120 kB
├ /_app 0 B 115 kB
├ ○ /404 3.1 kB 118 kB
├ λ /api/cars 0 B 115 kB
├ ○ /blog 8.5 kB 128 kB
├ ○ /blog/[slug] 12.3 kB 132 kB
└ ○ /charts 45.2 kB 165 kB
+ First Load JS shared by all 115 kB
├ chunks/framework-[hash].js 42 kB
├ chunks/main-[hash].js 28 kB
├ chunks/pages/_app-[hash].js 35 kB
└ chunks/webpack-[hash].js 10 kB
○ (Static) automatically rendered as static HTML
λ (Server) server-side renders at runtime
```
**Key Metrics:**
- **Size**: Page-specific JavaScript
- **First Load JS**: Total JS loaded for initial render
- **Shared chunks**: Code shared across pages
### Interactive Report
Open `client.html` or `server.html` in browser:
- **Large boxes**: Heavy dependencies
- **Colors**: Different packages
- **Hover**: See exact sizes
- **Click**: Drill down into dependencies
## Common Issues
### 1. Large Dependencies
```bash
# Find large packages
cd apps/web
pnpm list --depth=0 | sort -k2 -n
# Example output:
# lodash 1.2 MB
# moment 500 KB
# recharts 300 KB
# @heroui/react 250 KB
```
**Solutions:**
```typescript
// ❌ Import entire library
import _ from "lodash";
import moment from "moment";
// ✅ Import only what you need
import debounce from "lodash/debounce";
import groupBy from "lodash/groupBy";
// ✅ Use lighter alternatives
import { format } from "date-fns"; // Instead of moment
import dayjs from "dayjs"; // Smaller than moment
```
### 2. Duplicate Dependencies
```bash
# Check for duplicates
pnpm list <package-name>
# Example: Multiple versions of react
pnpm list react
# Output:
# @sgcarstrends/web
# ├── react@18.3.0
# └─┬ some-package
# └── react@18.2.0 # Duplicate!
```
**Solutions:**
```json
// package.json
{
"pnpm": {
"overrides": {
"react": "18.3.0",
"react-dom": "18.3.0"
}
}
}
```
### 3. Missing Code Splitting
```typescript
// ❌ Import heavy component directly
import Chart from "@/components/Chart";
export default function Page() {
return <Chart data={data} />;
}
// ✅ Dynamic import with code splitting
import dynamic from "next/dynamic";
const Chart = dynamic(() => import("@/components/Chart"), {
loading: () => <div>Loading chart...</div>,
ssr: false, // Client-side only if needed
});
export default function Page() {
return <Chart data={data} />;
}
```
### 4. Large JSON/Data Files
```typescript
// ❌ Import large JSON files
import brands from "@/data/car-brands.json"; // 500KB
// ✅ Load dynamically
export async function getBrands() {
const response = await fetch("/api/brands");
return response.json();
}
// ✅ Or use dynamic import
export async function getBrands() {
const { default: brands } = await import("@/data/car-brands.json");
return brands;
}
```
## Optimization Techniques
### 1. Tree Shaking
```typescript
// ✅ Named imports enable tree shaking
import { Button, Card } from "@heroui/react";
// ❌ Default import includes everything
import HeroUI from "@heroui/react";
```
**Verify tree shaking:**
```json
// package.json
{
"sideEffects": false // Enable aggressive tree shaking
}
// Or specify side-effect files
{
"sideEffects": ["*.css", "*.scss"]
}
```
### 2. Code Splitting by Route
```typescript
// Next.js automatically code-splits by route
// Each page becomes a separate chunk
// app/page.tsx -> chunk for /
// app/blog/page.tsx -> chunk for /blog
// app/charts/page.tsx -> chunk for /charts
// ✅ Additional manual splitting
const HeavyComponent = dynamic(() => import("./HeavyComponent"));
```
### 3. Lazy Loading
```typescript
// ✅ Load components on interaction
"use client";
import { useState } from "react";
import dynamic from "next/dynamic";
const CommentForm = dynamic(() => import("./CommentForm"));
export default function BlogPost() {
const [showComments, setShowComments] = useState(false);
return (
<div>
<article>{/* post content */}</article>
<button onClick={() => setShowComments(true)}>
Show Comments
</button>
{showComments && <CommentForm />}
</div>
);
}
```
### 4. Optimize Dependencies
```bash
# Replace heavy packages with lighter alternatives
# ❌ moment (500KB)
pnpm remove moment
# ✅ date-fns (30KB) or dayjs (7KB)
pnpm add date-fns
# ❌ lodash (full library)
# ✅ lodash-es (ESM with tree-shaking)
pnpm add lodash-es
# ❌ axios (for simple requests)
# ✅ native fetch or ky (12KB)
pnpm add ky
```
### 5. Image Optimization
```typescript
// ✅ Use Next.js Image component
import Image from "next/image";
export default function Logo() {
return (
<Image
src="/logo.png"
alt="Logo"
width={200}
height={100}
priority // For above-fold images
quality={75} // Reduce quality if acceptable
/>
);
}
// ✅ Use modern formats
// - WebP: 25-35% smaller than JPEG/PNG
// - AVIF: 50% smaller than JPEG (if supported)
```
### 6. Font Optimization
```typescript
// app/layout.tsx
import { Inter } from "next/font/google";
// ✅ Load only needed weights and subsets
const inter = Inter({
subsets: ["latin"],
weight: ["400", "600", "700"], // Only load what you use
display: "swap",
preload: true,
});
export default function RootLayout({ children }) {
return (
<html lang="en" className={inter.className}>
<body>{children}</body>
</html>
);
}
```
## Advanced Analysis
### Webpack Bundle Analyzer
```bash
# For custom webpack configs
pnpm add -D webpack-bundle-analyzer
# View detailed dependency tree
ANALYZE=true pnpm build
```
### Source Map Explorer
```bash
# Analyze source maps
pnpm add -D source-map-explorer
# Generate production build with source maps
pnpm build
# Analyze
npx source-map-explorer '.next/static/chunks/*.js'
```
### Bundle Buddy
```bash
# Visualize bundle relationships
npx bundle-buddy '.next/**/*.js.map'
```
## Monitoring Bundle Size
### CI Bundle Size Check
```yaml
# .github/workflows/bundle-size.yml
name: Bundle Size Check
on:
pull_request:
branches: [main]
jobs:
bundle-size:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm -F @sgcarstrends/web build
- name: Analyze bundle
run: |
BUNDLE_SIZE=$(du -sh apps/web/.next/static | cut -f1)
echo "Bundle size: $BUNDLE_SIZE"
# Fail if bundle exceeds limit
SIZE_KB=$(du -sk apps/web/.next/static | cut -f1)
if [ $SIZE_KB -gt 500 ]; then
echo "Bundle size exceeds 500KB!"
exit 1
fi
- name: Comment PR
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '📦 Bundle size: ${{ env.BUNDLE_SIZE }}'
})
```
### Bundle Size Tracking
```bash
# Track bundle size over time
echo "$(date +%Y-%m-%d),$(du -sk apps/web/.next/static | cut -f1)" >> bundle-history.csv
# Plot with gnuplot or spreadsheet
```
## Performance Budgets
### next.config.ts
```typescript
// apps/web/next.config.ts
const nextConfig: NextConfig = {
// Warn if bundles exceed limits
onDemandEntries: {
maxInactiveAge: 25 * 1000,
pagesBufferLength: 2,
},
// Set performance budgets
experimental: {
optimizeCss: true,
optimizePackageImports: ["@heroui/react", "recharts"],
},
};
```
### Lighthouse CI
```yaml
# .lighthouserc.json
{
"ci": {
"collect": {
"startServerCommand": "pnpm start",
"url": ["http://localhost:3000", "http://localhost:3000/charts"]
},
"assert": {
"assertions": {
"categories:performance": ["error", { "minScore": 0.9 }],
"resource-summary:script:size": ["error", { "maxNumericValue": 300000 }],
"total-byte-weight": ["error", { "maxNumericValue": 500000 }]
}
}
}
}
```
## Best Practices
### 1. Measure Before Optimizing
```bash
# ✅ Always measure first
ANALYZE=true pnpm build
# Identify actual bottlenecks
# Don't prematurely optimize
```
### 2. Prioritize Critical Path
```typescript
// ✅ Load critical resources first
// Above-fold content, essential JS/CSS
// ❌ Don't block initial render
// Defer analytics, chat widgets, etc.
```
### 3. Use External CDN
```typescript
// For heavy third-party libraries
// Load from CDN instead of bundling
// next.config.ts
const nextConfig: NextConfig = {
experimental: {
externalDir: true,
},
};
```
### 4. Regular Audits
```bash
# ✅ Run bundle analysis regularly
# - Before each release
# - When adding dependencies
# - After major refactors
ANALYZE=true pnpm build
```
## Troubleshooting
### Bundle Size Not Reducing
```bash
# Issue: Optimizations not working
# Solution: Check production build
# Ensure building for production
NODE_ENV=production pnpm build
# Verify minification
cat .next/static/chunks/main-*.js | head -n 1
# Should be minified (single line)
```
### Analyzer Not Opening
```bash
# Issue: Bundle analyzer not showing reports
# Solution: Check environment variable
# Ensure ANALYZE is set
echo $ANALYZE # Should print "true"
# Run explicitly
cd apps/web
ANALYZE=true pnpm build
# Check for errors in build output
```
### Large Unexplained Bundle
```bash
# Issue: Bundle larger than expected
# Solution: Investigate with source-map-explorer
pnpm build
npx source-map-explorer '.next/static/chunks/*.js' --html bundle-report.html
# Open bundle-report.html to see breakdown
```
## References
- Next.js Bundle Analyzer: https://www.npmjs.com/package/@next/bundle-analyzer
- Webpack Bundle Analyzer: https://github.com/webpack-contrib/webpack-bundle-analyzer
- Web.dev Bundle Size: https://web.dev/your-first-performance-budget/
- Next.js Optimization: https://nextjs.org/docs/app/building-your-application/optimizing
- Related files:
- `apps/web/next.config.ts` - Next.js configuration
- Root CLAUDE.md - Performance guidelines
## Best Practices Summary
1. **Analyze Regularly**: Check bundle size before releases
2. **Code Split**: Use dynamic imports for heavy components
3. **Tree Shake**: Use named imports, mark side effects
4. **Optimize Dependencies**: Replace heavy packages with lighter alternatives
5. **Lazy Load**: Defer non-critical resources
6. **Monitor**: Set up CI checks and performance budgets
7. **Use CDN**: Offload heavy third-party libraries
8. **Image Optimization**: Use Next.js Image with WebP/AVIFQuick Install
$
npx ai-builder add skill sgcarstrends/bundle-analysisDetails
- Type
- skill
- Author
- sgcarstrends
- Slug
- sgcarstrends/bundle-analysis
- Created
- 4d ago