Automatic optimizations
These are enabled by default and require no configuration:- Server Components — Used by default; no client-side JavaScript is sent for server-rendered components
- Code splitting — Route segments are automatically split; use lazy loading for Client Components and third-party libraries where appropriate
- Prefetching — Links entering the viewport are prefetched in the background for near-instant navigation
- Prerendering — Server and Client Components are prerendered at build time and the result is cached
- Data caching —
fetchrequests, server component renders, and static assets are cached automatically
During development
Routing and rendering
- Use Layouts to share UI across pages and enable partial rendering on navigation
- Use
<Link>for client-side navigation and prefetching—avoid<a>tags for internal links - Create custom error pages for catch-all errors and 404s
- Place
'use client'boundaries carefully to avoid unnecessarily increasing the client-side JavaScript bundle - Be aware that Request-time APIs (
cookies,headers,searchParams) opt the entire route into dynamic rendering - Wrap dynamic sections in
<Suspense>boundaries for streaming
Data fetching and caching
- Fetch data in Server Components to avoid unnecessary round-trips
- Fetch data in parallel where possible to reduce network waterfalls
- Verify which requests are being cached; use
unstable_cachefor requests that don’t usefetch - Do not call Route Handlers from Server Components—this creates an unnecessary server request
- Use the
public/directory for static assets to enable automatic caching
UI and accessibility
- Use Server Actions for form submissions, server-side validation, and error handling
- Add
app/global-error.tsxfor consistent error fallback UI across your app - Use
next/fontto host fonts alongside static assets—eliminates external network requests and reduces layout shift (CLS) - Use
<Image>to automatically optimize images, prevent layout shift, and serve in modern formats (WebP) - Use
<Script>to defer third-party scripts and prevent main thread blocking - Run
eslint-plugin-jsx-a11yto catch accessibility issues during development
Security
- Use data tainting to prevent sensitive values from being exposed to the client
- Verify authentication and authorization inside every Server Action—not just at the layout or page level
- Move database access to a
server-onlyData Access Layer - Ensure
.env.*files are in.gitignoreand only expose values withNEXT_PUBLIC_that are intentionally public - Consider adding a Content Security Policy
SEO and metadata
- Use the Metadata API for page titles, descriptions, and social sharing tags
- Generate Open Graph images for social sharing
- Create sitemaps and a robots.txt to help search engines crawl your pages
Type safety
- Use TypeScript throughout your application
- Enable the TypeScript plugin in your editor for real-time type checking
Before deploying to production
Run these checks before going live:Run Lighthouse
Open Chrome, navigate to your local production build, and run Lighthouse in incognito mode. This gives a simulated view of Core Web Vitals (LCP, CLS, INP) and identifies optimization opportunities.Use
useReportWebVitals to send real field data to your analytics:Analyze bundles
Use Additional tools:
@next/bundle-analyzer to identify large modules:- Import Cost — VS Code extension showing import sizes
- Bundle Phobia — Check npm package sizes
- bundlejs — Online bundle size checker
Monitoring
After deploying:- Set up error monitoring (Sentry, Datadog, etc.) to capture and alert on runtime errors
- Configure OpenTelemetry for performance tracing
- Monitor Core Web Vitals from real user data via your analytics platform
- Set up uptime monitoring and alerting
