Monitor JavaScript bundle size and execution performance. Use when tracking bundle size, identifying large chunks, or optimizing load performance.
Analyze JavaScript bundle size and runtime performance impact. Use when bundle size exceeds 200KB, long tasks are detected, or you need to implement code splitting and performance budgets.
/plugin marketplace add nexus-labs-automation/web-observability/plugin install nexus-labs-automation-web-observability@nexus-labs-automation/web-observabilityThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Monitor JavaScript bundle size and its impact on performance.
| Bundle Size | LCP Impact | INP Impact |
|---|---|---|
| <100KB | Minimal | Minimal |
| 100-300KB | Moderate | Noticeable |
| 300-500KB | Significant | Degraded |
| >500KB | Severe | Poor |
| 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 |
// 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,
}),
],
});
// 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
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 });
}
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 });
}
// 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'
)
);
// package.json
{
"size-limit": [
{
"path": "dist/**/*.js",
"limit": "200 KB"
},
{
"path": "dist/vendor*.js",
"limit": "100 KB"
}
]
}
// bundlewatch.config.json
{
"files": [
{
"path": "./dist/main*.js",
"maxSize": "150kB"
},
{
"path": "./dist/vendor*.js",
"maxSize": "100kB"
}
],
"ci": {
"trackBranches": ["main"],
"repoBranchBase": "main"
}
}
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);
}
| 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 |
skills/core-web-vitals for LCP/INP impactskills/hydration-performance for JS impact on hydrationskills/synthetic-monitoring for lab testingreferences/performance.md - Performance budgetsreferences/frameworks/*.md - Framework-specific optimizationUse when working with Payload CMS projects (payload.config.ts, collections, fields, hooks, access control, Payload API). Use when debugging validation errors, security issues, relationship queries, transactions, or hook behavior.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.