Agent skill
bundle-performance
Monitor JavaScript bundle size and execution performance. Use when tracking bundle size, identifying large chunks, or optimizing load performance.
Stars
163
Forks
31
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/development/bundle-performance-nexus-labs-automatio-web-observability
SKILL.md
Bundle Performance
Monitor JavaScript bundle size and its impact on performance.
Why Bundle Size Matters
| Bundle Size | LCP Impact | INP Impact |
|---|---|---|
| <100KB | Minimal | Minimal |
| 100-300KB | Moderate | Noticeable |
| 300-500KB | Significant | Degraded |
| >500KB | Severe | Poor |
Performance Budgets
| Metric | Good | Warning | Critical |
|---|---|---|---|
| Initial JS | <200KB | <500KB | >500KB |
| Per-route chunk | <100KB | <200KB | >200KB |
| Total JS | <500KB | <1MB | >1MB |
| First Load | <3s on 3G | <5s | >5s |
Build-Time Analysis
Vite
typescript
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
utils: ['lodash', 'date-fns'],
},
},
},
},
plugins: [
visualizer({
filename: 'dist/stats.html',
gzipSize: true,
brotliSize: true,
}),
],
});
Webpack (Next.js)
javascript
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer({
// your config
});
// Run: ANALYZE=true npm run build
Runtime Monitoring
Resource Timing API
typescript
function trackBundleLoading() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.name.endsWith('.js')) {
trackResourceLoad({
name: new URL(entry.name).pathname,
size_bytes: (entry as PerformanceResourceTiming).transferSize,
duration_ms: entry.duration,
type: 'javascript',
});
}
}
});
observer.observe({ type: 'resource', buffered: true });
}
Long Tasks API
typescript
function trackLongTasks() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
trackLongTask({
duration_ms: entry.duration,
start_time_ms: entry.startTime,
// Attribution for debugging
name: (entry as any).attribution?.[0]?.name || 'unknown',
container_type: (entry as any).attribution?.[0]?.containerType,
});
// Alert on very long tasks
if (entry.duration > 100) {
captureMessage('Long task detected', {
level: 'warning',
extra: {
duration_ms: entry.duration,
route: window.location.pathname,
},
});
}
}
});
observer.observe({ type: 'longtask', buffered: true });
}
Dynamic Import Tracking
typescript
// Track lazy-loaded chunks
async function trackedImport<T>(
importFn: () => Promise<T>,
chunkName: string
): Promise<T> {
const startTime = performance.now();
try {
const module = await importFn();
const duration = performance.now() - startTime;
trackChunkLoad({
chunk_name: chunkName,
duration_ms: duration,
success: true,
});
return module;
} catch (error) {
trackChunkLoad({
chunk_name: chunkName,
success: false,
error_type: error instanceof Error ? error.name : 'unknown',
});
throw error;
}
}
// Usage
const HeavyComponent = lazy(() =>
trackedImport(
() => import('./HeavyComponent'),
'HeavyComponent'
)
);
CI/CD Integration
Size Limit
json
// package.json
{
"size-limit": [
{
"path": "dist/**/*.js",
"limit": "200 KB"
},
{
"path": "dist/vendor*.js",
"limit": "100 KB"
}
]
}
bundlewatch
json
// bundlewatch.config.json
{
"files": [
{
"path": "./dist/main*.js",
"maxSize": "150kB"
},
{
"path": "./dist/vendor*.js",
"maxSize": "100kB"
}
],
"ci": {
"trackBranches": ["main"],
"repoBranchBase": "main"
}
}
Third-Party Script Tracking
typescript
function trackThirdPartyScripts() {
const entries = performance.getEntriesByType('resource') as PerformanceResourceTiming[];
const thirdParty = entries.filter((entry) => {
const url = new URL(entry.name);
return url.hostname !== window.location.hostname;
});
const summary = {
count: thirdParty.length,
total_size_bytes: thirdParty.reduce((sum, e) => sum + e.transferSize, 0),
total_duration_ms: thirdParty.reduce((sum, e) => sum + e.duration, 0),
scripts: thirdParty.map((e) => ({
url: e.name,
size_bytes: e.transferSize,
duration_ms: e.duration,
})),
};
trackThirdPartyImpact(summary);
}
Optimization Strategies
| Strategy | Impact | Implementation |
|---|---|---|
| Code splitting | High | Route-based chunks |
| Tree shaking | High | ES modules, sideEffects |
| Dynamic imports | High | Lazy load non-critical |
| Compression | High | Brotli/gzip |
| Modern/legacy | Medium | module/nomodule |
| Vendor chunking | Medium | Manual chunks |
| Preload critical | Medium | modulepreload |
Anti-Patterns
- Loading entire SDK in main bundle
- Not code-splitting routes
- Importing entire lodash/moment
- Missing tree-shaking (CommonJS)
- Not monitoring bundle size in CI
- Third-party scripts without budget
Related Skills
- See
skills/core-web-vitalsfor LCP/INP impact - See
skills/hydration-performancefor JS impact on hydration - See
skills/synthetic-monitoringfor lab testing
References
references/performance.md- Performance budgetsreferences/frameworks/*.md- Framework-specific optimization
Didn't find tool you were looking for?