Performance testing is crucial for ensuring your React application delivers an optimal user experience. Here’s a comprehensive guide to using Lighthouse and Web Vitals for measuring and improving your React app’s performance.
Core Web Vitals Implementation
1. Measuring Web Vitals in React
// web-vitals.js
import { getCLS, getFID, getLCP, getFCP, getTTFB } from 'web-vitals';
function sendToAnalytics({ name, delta, value, id }) {
// Send to your analytics platform
console.log(name, value);
// Example: Send to Google Analytics
if (typeof window.gtag === 'function') {
window.gtag('event', name, {
event_category: 'Web Vitals',
value: delta,
non_interaction: true,
});
}
}
// Initialize Web Vitals tracking
export function trackWebVitals() {
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getLCP(sendToAnalytics);
getFCP(sendToAnalytics);
getTTFB(sendToAnalytics);
}
2. Integrating with React
// index.js
import { trackWebVitals } from './web-vitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to measure performance, pass a function
if (process.env.NODE_ENV === 'production') {
trackWebVitals();
}
Lighthouse Testing
3. Automated Lighthouse CI
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm install
- run: npm run build
- uses: actions/upload-artifact@v3
with:
name: build
path: build
- name: Run Lighthouse
uses: treosh/lighthouse-ci-action@v9
with:
urls: |
http://localhost:3000
http://localhost:3000/dashboard
uploadArtifacts: true
temporaryPublicStorage: true
configPath: .lighthouserc.json
4. Lighthouse Configuration
// .lighthouserc.json
{
"ci": {
"collect": {
"numberOfRuns": 3,
"settings": {
"configPath": "./lighthouse-config.js",
"chromeFlags": "--no-sandbox --headless",
"onlyCategories": ["performance", "accessibility", "best-practices", "seo"]
}
},
"assert": {
"preset": "lighthouse:no-pwa",
"assertions": {
"first-contentful-paint": ["warn", {"maxNumericValue": 2000}],
"largest-contentful-paint": ["error", {"maxNumericValue": 2500}],
"cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
"interactive": ["error", {"maxNumericValue": 3500}],
"speed-index": ["error", {"maxNumericValue": 4300}]
}
}
}
}
Performance Optimization Techniques
5. Code Splitting in React
// Lazy load components
const Dashboard = React.lazy(() => import('./Dashboard'));
const Settings = React.lazy(() => import('./Settings'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
6. Image Optimization
// ResponsiveImage component
import { useState, useEffect } from 'react';
function ResponsiveImage({ src, alt, width, height }) {
const [loaded, setLoaded] = useState(false);
return (
<div style={{ position: 'relative', paddingBottom: `${(height/width)*100}%` }}>
<img
src={src}
alt={alt}
loading="lazy"
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
opacity: loaded ? 1 : 0,
transition: 'opacity 0.3s',
objectFit: 'cover'
}}
onLoad={() => setLoaded(true)}
/>
{!loaded && <Placeholder />}
</div>
);
}
Advanced Performance Monitoring
7. Custom Performance Metrics
// track.js
export function trackPerfMetric(name, startTime) {
const duration = performance.now() - startTime;
// Send to analytics
if (window.analytics) {
window.analytics.track(name, { duration });
}
// Log to console in development
if (process.env.NODE_ENV === 'development') {
console.log(`${name}: ${duration.toFixed(2)}ms`);
}
}
// Usage in components
function ProductList() {
useEffect(() => {
const start = performance.now();
return () => {
trackPerfMetric('ProductList render', start);
};
}, []);
return <div>...</div>;
}
8. Resource Timing API
// resource-timing.js
function analyzeResources() {
const resources = performance.getEntriesByType('resource');
const jsResources = resources.filter(r => r.initiatorType === 'script');
const report = {
totalResources: resources.length,
totalJS: jsResources.length,
jsSize: jsResources.reduce((sum, r) => sum + r.transferSize, 0),
slowResources: resources.filter(r => r.duration > 500)
};
return report;
}
// Send report every 5 minutes
setInterval(() => {
const report = analyzeResources();
navigator.sendBeacon('/perf-metrics', JSON.stringify(report));
}, 300000);
Performance Budgets
9. Performance Budget Configuration
// package.json
{
"performance-budget": {
"resources": [
{
"resourceType": "document",
"budget": 50
},
{
"resourceType": "script",
"budget": 200
},
{
"resourceType": "stylesheet",
"budget": 50
},
{
"resourceType": "image",
"budget": 300
},
{
"resourceType": "total",
"budget": 1000
}
],
"timings": [
{
"metric": "first-contentful-paint",
"budget": 2000
},
{
"metric": "largest-contentful-paint",
"budget": 2500
},
{
"metric": "cumulative-layout-shift",
"budget": 0.1
}
]
}
}
10. Enforcing Budgets in CI
# .github/workflows/performance-budget.yml
name: Performance Budget
on: [pull_request]
jobs:
performance:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm install
- run: npm run build
- name: Run Performance Budget Checker
uses: foo-software/lighthouse-check-action@master
with:
urls: http://localhost:3000
budgetPath: ./performance-budget.json
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
Visualization and Reporting
11. Lighthouse CI Server
# Install Lighthouse CI server
npm install -g @lhci/cli
# Run server
lhci server --storage.storageMethod=sqlite --storage.sqliteDatabasePath=./lhci.db
# Upload reports
lhci upload --serverBaseUrl=http://localhost:9001 --token=your-token
12. Grafana Dashboard for Web Vitals
{
"panels": [
{
"title": "Core Web Vitals",
"type": "graph",
"datasource": "Prometheus",
"targets": [
{
"expr": "avg(web_vitals_lcp)",
"legendFormat": "LCP"
},
{
"expr": "avg(web_vitals_fid)",
"legendFormat": "FID"
},
{
"expr": "avg(web_vitals_cls)",
"legendFormat": "CLS"
}
],
"thresholds": [
{
"value": 2500,
"color": "red",
"op": "gt",
"line": true
}
]
}
]
}
Best Practices Checklist
- Measure Continuously
- [ ] Set up Lighthouse CI
- [ ] Track Web Vitals in production
- [ ] Monitor performance budgets
- Optimize Loading
- [ ] Implement code splitting
- [ ] Optimize images
- [ ] Preload critical resources
- Improve Rendering
- [ ] Reduce unnecessary re-renders
- [ ] Virtualize long lists
- [ ] Use efficient CSS
- Monitor Third Parties
- [ ] Audit third-party scripts
- [ ] Lazy load non-critical scripts
- [ ] Set up resource timing monitoring
- Set Performance Goals
- [ ] Define performance budgets
- [ ] Establish Core Web Vitals targets
- [ ] Create team performance KPIs
- Visualize Data
- [ ] Set up dashboards
- [ ] Create performance reports
- [ ] Share metrics with stakeholders
- Automate Alerts
- [ ] Configure performance regressions alerts
- [ ] Set up budget violation notifications
- [ ] Create CI blocking rules