| name | performance-audit |
| description | Comprehensive performance audit workflow for web applications |
| license | MIT |
Performance Audit Skill
Use this skill when analyzing and optimizing application performance.
When to Use
- Before major releases
- After performance complaints
- Regular performance reviews
- Optimizing slow pages/features
- Setting performance budgets
Core Web Vitals
| Metric | Good | Needs Work | Poor | What It Measures |
|---|
| LCP | < 2.5s | < 4s | > 4s | Loading performance |
| FID | < 100ms | < 300ms | > 300ms | Interactivity |
| CLS | < 0.1 | < 0.25 | > 0.25 | Visual stability |
| INP | < 200ms | < 500ms | > 500ms | Responsiveness |
| TTFB | < 800ms | < 1.8s | > 1.8s | Server response |
Audit Workflow
Step 1: Establish Baseline
npx lighthouse https://example.com --output=json --output-path=baseline.json
Step 2: Analyze Bundle Size
npx webpack-bundle-analyzer stats.json
npx source-map-explorer 'dist/**/*.js'
ANALYZE=true npm run build
npx package-phobia lodash moment axios
Step 3: Profile Runtime
console.time('operation');
await expensiveOperation();
console.timeEnd('operation');
performance.mark('start');
await expensiveOperation();
performance.mark('end');
performance.measure('operation', 'start', 'end');
console.log(performance.getEntriesByName('operation'));
import { Profiler } from 'react';
<Profiler id="Component" onRender={onRenderCallback}>
<Component />
</Profiler>
function onRenderCallback(id, phase, actualDuration) {
console.log({ id, phase, actualDuration });
}
Optimization Techniques
Bundle Size
import _ from 'lodash';
import moment from 'moment';
import debounce from 'lodash/debounce';
import { format } from 'date-fns';
const Chart = dynamic(() => import('./Chart'), {
loading: () => <Skeleton />,
ssr: false,
});
import { Button, Input } from '@/components';
Images
import Image from 'next/image';
<Image
src="/hero.jpg"
width={1200}
height={600}
priority // For above-the-fold
placeholder="blur"
blurDataURL={blurHash}
sizes="(max-width: 768px) 100vw, 50vw"
/>
<Image
src="/product.jpg"
loading="lazy"
// ... other props
/>
Code Splitting
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<Loading />}>
<HeavyComponent />
</Suspense>
);
}
const AdminPanel = React.lazy(() =>
user.isAdmin ? import('./AdminPanel') : import('./UserPanel')
);
React Optimization
const sortedItems = useMemo(
() => items.sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
const handleClick = useCallback(
(id: string) => setSelected(id),
[]
);
const MemoizedList = React.memo(ItemList);
import { FixedSizeList } from 'react-window';
<FixedSizeList
height={400}
itemCount={10000}
itemSize={50}
>
{({ index, style }) => (
<div style={style}>{items[index].name}</div>
)}
</FixedSizeList>
Network Optimization
<link rel="prefetch" href="/api/user" />
<link rel="preload" href="/fonts/inter.woff2" as="font" crossOrigin />
const CACHE_TIME = 5 * 60 * 1000;
async function fetchWithCache(url: string) {
const cached = cache.get(url);
if (cached && Date.now() - cached.timestamp < CACHE_TIME) {
return cached.data;
}
const data = await fetch(url).then(r => r.json());
cache.set(url, { data, timestamp: Date.now() });
return data;
}
import useSWR from 'swr';
function Profile() {
const { data, error, isLoading } = useSWR('/api/user', fetcher, {
revalidateOnFocus: false,
dedupingInterval: 60000,
});
}
Database Optimization
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at DESC);
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 123;
SELECT * FROM users;
SELECT u.*, o.* FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE u.id IN (1, 2, 3);
Performance Budget
{
"bundles": [
{ "name": "main", "maxSize": "100 KB" },
{ "name": "vendor", "maxSize": "150 KB" }
],
"metrics": {
"LCP": "2500",
"FID": "100",
"CLS": "0.1",
"TTFB": "800"
},
"resourceSizes": {
"script": "300 KB",
"image": "500 KB",
"font": "100 KB"
}
}
module.exports = {
ci: {
assert: {
assertions: {
'first-contentful-paint': ['error', { maxNumericValue: 2000 }],
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
'total-blocking-time': ['error', { maxNumericValue: 300 }],
},
},
},
};
Audit Report Template
## Performance Audit Report
### Summary
| Metric | Current | Target | Status |
|--------|---------|--------|--------|
| LCP | 3.2s | < 2.5s | ⚠️ |
| FID | 85ms | < 100ms | ✅ |
| CLS | 0.05 | < 0.1 | ✅ |
| Bundle Size | 450KB | < 300KB | ❌ |
### Top Issues
#### 1. Large JavaScript Bundle (450KB)
**Impact**: +1.2s load time on 3G
**Cause**: Full lodash import, unoptimized images
**Fix**: Tree-shake lodash, lazy load charts
#### 2. Slow LCP (3.2s)
**Impact**: Poor user experience, SEO penalty
**Cause**: Hero image not optimized, render-blocking CSS
**Fix**: Add priority to hero image, inline critical CSS
### Optimization Roadmap
| Priority | Task | Impact | Effort |
|----------|------|--------|--------|
| 1 | Tree-shake lodash | -45KB | Low |
| 2 | Lazy load charts | -80KB | Medium |
| 3 | Optimize images | -0.8s LCP | Low |
| 4 | Add caching | -200ms | Medium |
### Expected Results
- Bundle size: 450KB → 280KB (-38%)
- LCP: 3.2s → 2.1s (-34%)
- Lighthouse score: 65 → 90
Tools Reference
| Tool | Purpose | Command |
|---|
| Lighthouse | Full audit | npx lighthouse URL |
| Bundle Analyzer | Bundle composition | npx webpack-bundle-analyzer |
| Source Map Explorer | Module sizes | npx source-map-explorer |
| WebPageTest | Real-world testing | webpagetest.org |
| Chrome DevTools | Profiling | Performance tab |