We've been running Next.js 15 in production since February 2026 — on everything from marketing sites to enterprise SaaS dashboards with 50,000 DAU. The framework has matured significantly, but the performance playbook has also changed enough that patterns from Next.js 13/14 can actually hurt you now.
This is a practitioner's guide — not a translation of the docs, but the specific decisions we've changed, the mistakes we've made, and the things that genuinely moved the needle on Core Web Vitals.
Server Components: the mindset shift that matters
The biggest mistake we see in Next.js 15 codebases is treating Server Components as an optimisation to apply after the fact — 'let's move this to a server component for performance'. That's backwards.
The correct mental model: assume everything is a Server Component unless it explicitly needs to be a client component. A component needs to be a client component if and only if it uses: browser APIs, React hooks (useState, useEffect, etc.), event listeners, or third-party libraries that depend on these.
In practice, this means your data-fetching logic, static content, and most of your layout tree should be server components by default. The client component boundary should be as deep in the tree as possible.
Partial Prerendering: what it actually does
PPR is the most misunderstood new feature. It lets you statically render the shell of a page (instant, served from CDN) while streaming in dynamic parts (authenticated data, personalised content) as they resolve.
The practical implication: your page's perceived load time drops dramatically because users see the layout immediately, even if the data hasn't arrived yet. For dashboards and data-heavy pages, this can cut LCP by 40-60%.
The gotcha: PPR requires explicit Suspense boundaries around your dynamic content. If you're not already using Suspense with meaningful loading UI, PPR won't help you — it'll just show blank spaces until data loads.
Turbopack in production: what we found
Turbopack is stable in Next.js 15. Our dev builds are 3-4x faster. But there are some important production considerations:
- Some older webpack plugins (particularly legacy CSS modules setups and certain SVG loaders) are not yet Turbopack-compatible. Audit your dependencies before switching.
- Bundle analysis tools that depend on webpack internals don't work with Turbopack. Use the built-in Next.js bundle analyser or Vercel's built-in analytics instead.
- framer-motion v11 requires --webpack flag or specific import patterns with Turbopack due to ESM handling. Pin your framer-motion version and test the specific animation components you use.
Image optimisation: still the biggest quick win
Despite all the framework advances, improper image handling remains the #1 cause of poor LWS and CLS scores we see in audits. Three rules we enforce on every project:
- Always specify width and height on next/image. 'fill' layout is fine for hero sections, but for content images, explicit dimensions prevent layout shift.
- Use priority={true} for above-the-fold images only. LCP is determined by the single largest above-the-fold element — preload that one, not your entire image inventory.
- Set quality={85} rather than the default 75 for product/portfolio images. The 10-point quality increase is ~15% larger files but meaningfully better for images where compression artefacts are noticeable.
The one metric that tells you everything
If you're overwhelmed by Core Web Vitals and don't know where to start, optimise for INP (Interaction to Next Paint). It replaced FID as a Core Web Vital and it's the one metric most correlated with user satisfaction on interactive apps.
The primary cause of poor INP in Next.js apps is large JavaScript bundles executing on the main thread. Use the Next.js bundle analyser, find the 5 largest client-side packages, and ask: do they need to be loaded on every page, or can they be lazy-loaded on demand?
Most teams find 40-60% of their client-side JS is loaded on pages where it's never used.
Want expert help with Software Development?
Tell us about your project — we reply within one business day.
Start a conversationMore articles


