Performance Optimization·7 min

Why Is My Next.js Site Slow? 7 Common Causes and Fixes

By Bahaj Abderrazak·Published August 11, 2026·Updated September 28, 2026

Next.js should be fast out of the box, so when it isn't, something specific is usually wrong. Here are the 7 causes I find most often, with code to fix each one.

Next.js ships with a lot of performance features turned on: server-side rendering, automatic code splitting, image optimization, font handling. So when a Next.js site is slow, the framework is rarely the problem. Usually someone (often me, on an older project) has switched off one of those defaults without noticing.

After auditing a fair number of slow Next.js sites, the same seven causes keep showing up. Start with the first three, since they account for most of the slow pages I see.

1. Using a plain <img> tag instead of next/image

A regular <img> gets none of the optimization Next.js offers. No resizing for the visitor's screen, no conversion to WebP or AVIF, no lazy loading. On a page with a big hero photo, this alone can wreck your Largest Contentful Paint (LCP).

Swap it for the Image component and always give it a width and height, so the browser reserves the space before the file arrives:

import Image from "next/image";

<Image
  src="/hero.jpg"
  alt="Dashboard preview"
  width={1200}
  height={630}
  priority // only for the main image above the fold
/>

Reserving that space also prevents layout shift, which is the other half of Core Web Vitals (see Core Web Vitals Explained). For formats, lazy loading, and CDN basics beyond what Next.js handles for you, I wrote a separate guide on image and asset optimization.

2. A JavaScript bundle that's too big

The browser has to download and run all your JavaScript before a page becomes interactive. Two habits bloat it fast: importing a whole library for one small function, and pulling heavy components into pages that barely use them.

Use next/dynamic for anything that isn't needed on first paint, like modals, charts, or a rich text editor:

import dynamic from "next/dynamic";

const Chart = dynamic(() => import("../components/Chart"), {
  ssr: false,
  loading: () => <p>Loading chart...</p>,
});

Also run @next/bundle-analyzer once in a while. It usually turns up one dependency that quietly added 200 KB.

3. Rendering on the client when the server could do it

If you're on the App Router, components are Server Components by default. Adding "use client" at the top of a file sends that component's JavaScript to every visitor, even if the component only displays text.

A good rule: keep pages and layouts on the server, and push "use client" down to the smallest piece that needs it, like a search box, a dropdown, or a form. A blog post body doesn't need to hydrate. Its comment form does.

4. Fetching data one request at a time

Say a page needs products, reviews, and user info. If you await each one in sequence, the total wait is the sum of all three. Fetch them together and you only wait for the slowest one:

// Slow: each request waits for the previous one
const products = await getProducts();
const reviews = await getReviews();
const user = await getUser();

// Faster: all three start at the same time
const [products, reviews, user] = await Promise.all([
  getProducts(),
  getReviews(),
  getUser(),
]);

This is one of the easiest wins for Time to First Byte (TTFB), and it takes about two minutes to fix.

5. Custom fonts that block text or make it jump

Loading fonts from a <link> tag or a CSS @import often causes one of two problems. Either the text is invisible until the font downloads, or it appears in a fallback font and then jumps when the real one loads.

next/font fixes both. It self-hosts the font, preloads it, and matches the fallback's metrics so nothing moves:

import { Inter } from "next/font/google";

const inter = Inter({ subsets: ["latin"], display: "swap" });

6. Refetching data that barely changes

A list of categories or a site settings object doesn't need to be fetched fresh on every request. If nothing caches it, every visitor pays for a round trip to your API or database.

Use static generation with revalidation (ISR) for content that changes now and then:

const res = await fetch("https://api.example.com/categories", {
  next: { revalidate: 3600 }, // refresh at most once per hour
});

The page is served instantly from cache and quietly rebuilt in the background.

7. Third-party scripts loaded carelessly

Analytics, chat widgets, cookie banners, and ad pixels are a common reason a fast site turns slow. A plain <script> tag can block rendering while it waits on someone else's server.

Load them through next/script with a strategy that matches how important they are:

import Script from "next/script";

<Script
  src="https://example.com/widget.js"
  strategy="lazyOnload"
/>

Use afterInteractive for things like analytics and lazyOnload for things like chat widgets. Neither should compete with your actual content.

How to find out which one is hurting your site

Run the page through PageSpeed Insights or Lighthouse. If the report reads like a foreign language, see A Non-Technical Guide to Reading Your Lighthouse / PageSpeed Report.

The warnings map closely to the causes above:

  • "Properly size images" or "Serve images in next-gen formats" points to cause 1.

  • "Reduce unused JavaScript" points to causes 2 and 3.

  • "Reduce initial server response time" points to causes 4 and 6.

  • "Ensure text remains visible during webfont load" points to cause 5.

  • "Reduce the impact of third-party code" points to cause 7.

Fix them in that order of impact, re-run the test after each change, and you'll see which ones actually moved your score. Once the score looks good, my pre-launch audit checklist covers the performance and accessibility checks I run before any site goes live.

If you'd rather have someone go through your whole site and fix these properly, take a look at my performance optimization service, or get in touch with the URL of the slow site and I'll tell you where I'd start.

Next.jsPerformance OptimizationCore Web VitalsWebsite PerformanceFrontend Development

Related articles

Let's begin

Have an idea worth building?

Tell me what you are creating, what stage the project is at, and where you need technical support — I will respond with practical next steps.