Performance Guide
Optimize and monitor Constellation's performance.
Core Web Vitals
Monitor these three key metrics:
1. Largest Contentful Paint (LCP)
- Measures: Page load speed (when main content is visible)
- Target: < 2.5 seconds
- Tips:
- Optimize images with Next.js Image component
- Minimize JavaScript
- Use Server Components for initial load
- Implement code splitting
2. Interaction to Next Paint (INP)
- Measures: Responsiveness to user interactions
- Target: < 200 milliseconds
- Tips:
- Minimize JavaScript execution time
- Defer non-critical JavaScript
- Optimize event handlers
- Use React.memo for expensive components
3. Cumulative Layout Shift (CLS)
- Measures: Visual stability (unexpected layout changes)
- Target: < 0.1
- Tips:
- Set fixed sizes for images and videos
- Avoid inserting content above existing content
- Use transforms instead of changing dimensions
Measurement Tools
Chrome DevTools
# Open DevTools (F12)
# Go to Performance tab
# Record page load
# Analyze results
Lighthouse
# Built into Chrome DevTools
# Generates performance report
# Provides specific recommendations
# Target: 90+ score
Web Vitals
// src/lib/web-vitals.ts
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
export function reportWebVitals(metric: any) {
console.log(metric);
// Send to analytics service
}
getCLS(reportWebVitals);
getFID(reportWebVitals);
getFCP(reportWebVitals);
getLCP(reportWebVitals);
getTTFB(reportWebVitals);
Optimization Strategies
Code Splitting
// โ
GOOD - Automatic route-based splitting
// Each route gets its own bundle
// โ
GOOD - Manual component splitting
import dynamic from 'next/dynamic';
const HeavyEditor = dynamic(() => import('./Editor'), {
loading: () => <div>Loading editor...</div>,
});
export function Dashboard() {
return (
<>
<Sidebar />
<HeavyEditor />
</>
);
}
Image Optimization
import Image from 'next/image';
// โ
GOOD - Optimized with Next.js Image
<Image
src="/image.jpg"
alt="Description"
width={800}
height={600}
priority={false}
loading="lazy"
placeholder="blur"
blurDataURL="data:image/jpeg..."
/>
// โ BAD - Regular img tag
<img src="/image.jpg" />
Component Memoization
import { memo, useMemo, useCallback } from 'react';
// โ
GOOD - Memoized component
export const ListItem = memo(function ListItem({ item, onSelect }) {
return (
<div onClick={() => onSelect(item)}>
{item.name}
</div>
);
});
// โ
GOOD - Memoized callback
const handleSelect = useCallback((item) => {
setSelected(item);
}, []);
// โ
GOOD - Memoized expensive computation
const total = useMemo(() => {
return items.reduce((sum, item) => sum + item.price, 0);
}, [items]);
Bundle Analysis
# Analyze bundle size
npm run build:analyze
# Look for:
# - Large third-party libraries
# - Duplicate dependencies
# - Unused code
Server Components
// โ
GOOD - Server Component (no JavaScript sent)
export default async function Dashboard() {
const data = await fetchData();
return <div>{data}</div>;
}
// โ BAD - Client Component (sends all JS to browser)
'use client';
export default function Dashboard() {
const [data, setData] = useState();
useEffect(() => {
fetchData().then(setData);
}, []);
return <div>{data}</div>;
}
Caching Strategies
Browser Caching
// next.config.js
module.exports = {
headers: async () => {
return [
{
source: '/images/:path*',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
},
];
},
};
Data Caching
// Use React cache for per-request deduplication
import { cache } from 'react';
export const getUser = cache(async (id: string) => {
return apiClient.get(`/users/${id}`);
});
// Multiple calls in same request only hit API once
const user1 = await getUser('123');
const user2 = await getUser('123'); // Cached result
Query String Caching
// Avoid query strings for static assets
// โ BAD
<img src="/image.jpg?v=1" />
// โ
GOOD
<Image src="/image-v1.jpg" />
Monitoring
Monitoring Setup
// src/lib/monitoring.ts
export function trackPerformance() {
// Report to analytics service
const metrics = {
FCP: 0,
LCP: 0,
INP: 0,
CLS: 0,
};
fetch('/api/metrics', {
method: 'POST',
body: JSON.stringify(metrics),
});
}
// Call on page load
useEffect(() => {
trackPerformance();
}, []);
Analytics Dashboard
Track: - Page load times - Core Web Vitals - Error rates - User interactions - API response times
Database Query Optimization
N+1 Query Prevention
// โ BAD - N+1 queries
const workspaces = await db.workspace.findMany();
for (const workspace of workspaces) {
const users = await db.user.findMany({
where: { workspaceId: workspace.id },
});
}
// โ
GOOD - Single query with relation
const workspaces = await db.workspace.findMany({
include: { users: true },
});
Query Indexing
// Add indexes to frequently queried fields
// In your database schema:
// CREATE INDEX idx_workspace_user ON workspace(user_id);
// CREATE INDEX idx_document_workspace ON document(workspace_id);
API Optimization
Response Compression
// next.config.js
module.exports = {
compress: true, // Enable gzip compression
};
Request Batching
// Group multiple API calls
async function fetchDashboardData() {
const [user, workspaces, notifications] = await Promise.all([
apiClient.get('/user'),
apiClient.get('/workspaces'),
apiClient.get('/notifications'),
]);
return { user, workspaces, notifications };
}
Pagination
// Load data in chunks, not all at once
const { data: items, total } = await apiClient.get('/items', {
params: { page: 1, limit: 20 },
});
Lazy Loading
Route-Based
// Automatically lazy loaded by Next.js
// src/app/heavy-feature/page.tsx
// Only loads when user navigates to route
Component-Based
const Editor = dynamic(() => import('./Editor'), {
loading: () => <Skeleton />,
});
Image Lazy Loading
<Image
src="/image.jpg"
loading="lazy" // Load only when visible
alt="Description"
/>
Performance Checklist
Development
- [ ] Use Chrome DevTools to profile code
- [ ] Check Console for errors/warnings
- [ ] Monitor Network tab for slow requests
- [ ] Test on slow network (DevTools)
- [ ] Test on slow devices (DevTools)
Build
- [ ]
bun run buildsucceeds - [ ]
npm run build:analyzeshows reasonable size - [ ] No unused dependencies
- [ ] Code splitting working
Before Deploy
- [ ] Lighthouse score > 90
- [ ] Bundle size < 200KB (gzipped)
- [ ] No performance warnings
- [ ] Mobile performance tested
- [ ] Real device testing done
Production
- [ ] Monitor Core Web Vitals
- [ ] Track error rates
- [ ] Monitor API response times
- [ ] Set up alerts for degradation
- [ ] Regular performance reviews
Common Performance Issues
Slow Initial Load
Causes: - Large JavaScript bundle - Unoptimized images - Render-blocking resources - Slow API requests
Solutions: - Code split and defer non-critical JS - Optimize images - Use async/defer attributes - Implement caching
Janky Interactions
Causes: - Expensive computations in event handlers - Too many re-renders - Blocking main thread
Solutions:
- Use requestAnimationFrame
- Memoize components/functions
- Move heavy work to Web Workers
- Debounce expensive operations
Memory Leaks
Causes: - Event listeners not removed - Timers not cleared - Circular references
Solutions:
- Clean up in useEffect return
- Clear timers
- Remove event listeners
Tools
Bundle Analyzer
npm run build:analyze
Chrome DevTools
- Performance tab
- Lighthouse
- Network tab
- Coverage tab
Web Vitals Library
npm install web-vitals
Related Documentation
- ๐ DEPLOYMENT.md - Deployment optimization
- ๐๏ธ ARCHITECTURE.md - Architecture for performance
- ๐งช TESTING.md - Performance testing
Last Updated: January 2026
Tools: web.dev/performance