Skip to main content
Before deploying your Next.js application, there are optimizations and patterns that improve performance, security, and user experience. This checklist covers what Next.js handles automatically and what you should configure manually.

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 cachingfetch requests, 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_cache for requests that don’t use fetch
  • 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.tsx for consistent error fallback UI across your app
  • Use next/font to 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-a11y to 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-only Data Access Layer
  • Ensure .env.* files are in .gitignore and only expose values with NEXT_PUBLIC_ that are intentionally public
  • Consider adding a Content Security Policy

SEO and metadata

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:
1

Build and test locally

next build   # catch build errors
next start   # test in a production-like environment
2

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:
'use client'

import { useReportWebVitals } from 'next/web-vitals'

export function WebVitals() {
  useReportWebVitals((metric) => {
    console.log(metric)
  })
  return null
}
3

Analyze bundles

Use @next/bundle-analyzer to identify large modules:
npm install @next/bundle-analyzer
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
})

module.exports = withBundleAnalyzer({})
ANALYZE=true next build
Additional tools:
4

Review security headers

Verify your application sends appropriate security headers. At minimum:
async headers() {
  return [
    {
      source: '/(.*)',
      headers: [
        { key: 'X-Content-Type-Options', value: 'nosniff' },
        { key: 'X-Frame-Options', value: 'DENY' },
        { key: 'X-XSS-Protection', value: '1; mode=block' },
        { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
      ],
    },
  ]
}
5

Check environment variables

  • All secrets are stored in environment variables, not hardcoded
  • .env.* files are in .gitignore
  • Only intentionally public values use the NEXT_PUBLIC_ prefix
  • Production environment variables are set correctly in your deployment platform

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

Build docs developers (and LLMs) love