Troubleshooting

How I fix Next.js build and performance issues in real projects

A repeatable workflow for diagnosing slow builds, heavy pages and confusing runtime behavior.

By Nadir AMMI SAID 10 min read

Start with the exact symptom

When someone says that a Next.js site is slow, I do not change the first component I see. I write down which route is slow, for whom, in which environment and whether the problem is build time, server response, download size or interaction.

That distinction saves time. A five-second local build and a five-second browser load are different problems. They can share a dependency, but they need different measurements and different fixes.

  • Record the failing route
  • Separate build, server and browser symptoms
  • Write down what changed before the regression

Read the build output

The production build is a useful diagnostic report. I look for routes that became dynamic unexpectedly, large client bundles, image warnings and dependency messages. I compare the current output with a known good run instead of treating every warning as the cause.

Node and package versions matter too. A project can compile on one machine and fail in CI because the runtime, lockfile or native package differs. Before editing application code, I confirm that the environment matches the project requirements.

  • Run the production build locally
  • Check the Node.js version
  • Compare route and bundle output

Trace the server and client boundary

A common Next.js performance mistake is turning a large page into a client component because one small interaction needs state. That can move data fetching and rendering work into the browser. I keep the page and data on the server when possible, then isolate the interactive piece.

I also look at repeated requests and serialization. Fetching the same data in several components or sending a large object through a client boundary can be more expensive than the visible UI suggests.

  • Keep client components narrow
  • Avoid duplicate data fetching
  • Send only the data an interaction needs

Fix the largest measured cost first

Once the bottleneck is clear, I make one focused change. It may be an image size, a client boundary, a query, a dependency or a cache setting. I avoid combining five optimizations because the result becomes impossible to explain and harder to roll back.

The fix should improve a user-visible metric or remove a concrete build problem. Cleaner code is valuable, but it is not proof that the original issue was solved.

  • Change one expensive thing at a time
  • Prefer removing work over hiding it
  • Keep the patch easy to revert

Prove and document the fix

I rerun the command that first exposed the issue, then repeat the browser or route measurement. If the result is better, I record why. If it is not, I undo the assumption and test the next boundary.

A durable fix leaves a guardrail: a test, a build check, a bundle budget, a note in the runbook or simply a clearer ownership boundary. Debugging is finished when the same failure is less likely to return, not when the terminal turns green once.

Next.jsReactVercel

Keep reading

Back to the journal