frontend-bundle-analysis-and-optimization
Bundle analysis and optimization strategies for Next.js applications using Webpack and Turbopack. PROACTIVELY activate for: (1) Analyzing bundle size, (2) Implementing code splitting, (3) Configuring tree shaking, (4) Dynamic imports, (5) Webpack/Turbopack configuration. Triggers: "bundle size", "webpack", "turbopack", "code splitting", "tree shaking", "lazy loading", "bundle analyzer", "dynamic import"
What this skill does
# Frontend Bundle Analysis and Optimization
This skill provides actionable strategies for analyzing and reducing JavaScript bundle size using Webpack or Turbopack, focusing on code splitting, tree shaking, and dependency analysis.
## Bundle Analysis with @next/bundle-analyzer
Visual bundle analysis is the first step in any optimization effort. It shows which modules contribute most to bundle size.
### Setup
```bash
npm install @next/bundle-analyzer --save-dev
```
```js
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
})
module.exports = withBundleAnalyzer({
// Your Next.js config
})
```
### Usage
```bash
ANALYZE=true npm run build
```
This opens an interactive treemap showing:
- Which dependencies are largest
- Duplicate packages across chunks
- Opportunities for code splitting
### Reading the Treemap
- **Large blocks**: Heavy dependencies (consider alternatives or lazy loading)
- **Duplicate colors**: Same package in multiple chunks (configure splitChunks)
- **Vendor chunks**: Third-party libraries (good candidates for caching)
## Dynamic Imports
Dynamic imports enable code splitting, loading JavaScript only when needed.
### Component-Based Code Splitting
```tsx
// BAD: Loads heavy component immediately
import HeavyChart from '@/components/HeavyChart'
export default function Dashboard() {
return <HeavyChart data={data} />
}
// GOOD: Loads only when rendered
import dynamic from 'next/dynamic'
const HeavyChart = dynamic(() => import('@/components/HeavyChart'), {
loading: () => <ChartSkeleton />,
ssr: false, // Disable SSR if component uses browser APIs
})
export default function Dashboard() {
return <HeavyChart data={data} />
}
```
### Conditional Loading
```tsx
// Load component only when needed
import dynamic from 'next/dynamic'
import { useState } from 'react'
const AdminPanel = dynamic(() => import('@/components/AdminPanel'))
export default function Dashboard({ isAdmin }: { isAdmin: boolean }) {
const [showAdmin, setShowAdmin] = useState(false)
return (
<div>
<h1>Dashboard</h1>
{isAdmin && (
<button onClick={() => setShowAdmin(true)}>
Show Admin Panel
</button>
)}
{/* Only loads when button is clicked */}
{showAdmin && <AdminPanel />}
</div>
)
}
```
### Named Exports
```tsx
// For named exports, use an object with 'default'
const DynamicComponent = dynamic(() =>
import('@/components/Hello').then((mod) => mod.Hello)
)
```
### Multiple Components
```tsx
// BAD: Import entire library
import { ComponentA, ComponentB } from 'heavy-library'
// GOOD: Split into separate chunks
const ComponentA = dynamic(() => import('heavy-library/ComponentA'))
const ComponentB = dynamic(() => import('heavy-library/ComponentB'))
```
### With Custom Loading State
```tsx
const HeavyEditor = dynamic(() => import('@/components/RichTextEditor'), {
loading: () => (
<div className="editor-skeleton">
<div className="toolbar-skeleton" />
<div className="content-skeleton" />
</div>
),
ssr: false,
})
```
## Route-Based Code Splitting
Next.js automatically code-splits by route, but you can optimize further.
### Lazy Route Components
```tsx
// app/dashboard/page.tsx
import dynamic from 'next/dynamic'
// Heavy components loaded only on this route
const Analytics = dynamic(() => import('@/components/Analytics'))
const UserTable = dynamic(() => import('@/components/UserTable'))
export default function DashboardPage() {
return (
<div>
<Analytics />
<UserTable />
</div>
)
}
```
### Shared Layouts
```tsx
// app/layout.tsx
// Common layout code shared across routes
export default function RootLayout({ children }) {
return (
<html>
<body>
<Header /> {/* Shared, in main bundle */}
{children} {/* Route-specific, code-split */}
<Footer /> {/* Shared, in main bundle */}
</body>
</html>
)
}
```
## Webpack splitChunks Configuration
Fine-tune how code is divided into chunks for optimal caching and loading.
### Default Next.js Configuration
Next.js has sensible defaults, but you can customize:
```js
// next.config.js
module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.optimization.splitChunks = {
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
// Vendor chunk for node_modules
vendor: {
name: 'vendor',
chunks: 'all',
test: /node_modules/,
priority: 20,
},
// Common chunk for shared code
common: {
name: 'common',
minChunks: 2,
chunks: 'all',
priority: 10,
reuseExistingChunk: true,
enforce: true,
},
},
}
}
return config
},
}
```
### Framework Chunk
```js
// Separate React/Next.js into their own chunk for better caching
cacheGroups: {
framework: {
name: 'framework',
test: /[\\/]node_modules[\\/](react|react-dom|next)[\\/]/,
priority: 40,
enforce: true,
},
}
```
### Library-Specific Chunks
```js
// Create separate chunk for a heavy library
cacheGroups: {
charts: {
name: 'charts',
test: /[\\/]node_modules[\\/](recharts|d3|chart\.js)[\\/]/,
priority: 30,
enforce: true,
},
}
```
### Size-Based Splitting
```js
splitChunks: {
chunks: 'all',
maxSize: 244 * 1024, // 244 KB max chunk size
minSize: 20 * 1024, // 20 KB min chunk size
}
```
## Tree Shaking and Side Effects
Tree shaking eliminates unused code, but requires proper configuration.
### Package.json sideEffects
```json
{
"name": "my-library",
"sideEffects": false
}
```
This tells bundlers that no files have side effects, enabling aggressive tree shaking.
### Specific Side Effects
```json
{
"sideEffects": [
"*.css",
"*.scss",
"./src/polyfills.ts"
]
}
```
### Import Best Practices
```ts
// BAD: Imports entire library
import _ from 'lodash'
const result = _.debounce(fn, 100)
// GOOD: Import specific function
import debounce from 'lodash/debounce'
const result = debounce(fn, 100)
// EVEN BETTER: Use tree-shakeable alternative
import { debounce } from 'lodash-es'
const result = debounce(fn, 100)
```
### Named Imports
```ts
// BAD: Entire date-fns library loaded
import * as dateFns from 'date-fns'
dateFns.format(new Date(), 'yyyy-MM-dd')
// GOOD: Only format function loaded
import { format } from 'date-fns'
format(new Date(), 'yyyy-MM-dd')
```
### Barrel File Issues
```ts
// utils/index.ts - BAD: Barrel file prevents tree shaking
export * from './stringUtils'
export * from './arrayUtils'
export * from './dateUtils'
// GOOD: Import directly from specific files
import { formatString } from '@/utils/stringUtils'
```
## Turbopack Considerations
Turbopack is Next.js's Rust-based bundler, significantly faster than Webpack.
### Enable for Development
```bash
next dev --turbo
```
### Current Status (as of Next.js 15)
- **Development**: Stable, recommended
- **Production**: Experimental, opt-in
### Enable for Production (Experimental)
```js
// next.config.js
module.exports = {
experimental: {
turbo: {},
},
}
```
### Benefits
- **5-10x faster cold starts** in development
- **Faster HMR** (Hot Module Replacement)
- **Lower memory usage**
### Limitations
- Some Webpack plugins not yet supported
- Custom Webpack config requires migration
### Migration Strategy
1. Test in development first: `next dev --turbo`
2. Verify all features work correctly
3. Check for unsupported Webpack plugins
4. Migrate custom configurations
5. Test production builds thoroughly before deploying
## Dependency Analysis
### Find Heavy Dependencies
```bash
# Install dependency analyzer
npm install -g depcheck
# Find unused dependencies
depcheck
# Check bundle impact
npm install -g bundle-phobia-clRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.