How to Fix: Next js is double rendering components

5 min read

If your Next.js app appears to render components twice in development, the behavior is usually not a rendering bug in your app at all. In most cases, it is React Strict Mode intentionally mounting, unmounting, and re-mounting components to expose unsafe side effects during development.

Understanding the Root Cause

The issue shown in this reproduction is typically caused by React 18 development behavior inside Next.js. When Strict Mode is enabled, React intentionally runs component initialization paths more than once in development. This includes:

  • Calling component functions twice
  • Running useEffect setup and cleanup more than once during development checks
  • Re-mounting parts of the tree to detect side effects that are not resilient to re-renders

This does not mean production users will see the same behavior. In production builds, React does not perform the same extra development checks.

In a Next.js app, this can be confusing because logs in the console make it look like the framework is rendering the page twice. What is actually happening is one of these scenarios:

  • reactStrictMode is enabled in Next.js configuration
  • You are testing in development mode with bun dev or next dev
  • Your component contains side effects directly in the render body, making the double invocation more visible

For example, code like this makes the problem look worse:

export default function Example() {
  console.log('rendered');
  return <div>Hello</div>;
}

In development with Strict Mode, that console log may appear twice. That is expected.

The deeper reason is that React wants developers to detect code that is unsafe under concurrent rendering. If your component performs mutations, network calls, subscriptions, or object initialization in the wrong place, the extra development render helps expose it early.

Step-by-Step Solution

The right fix depends on whether you want to understand the behavior, verify production output, or disable the extra development check.

1. Confirm it only happens in development

First, run a production build and compare behavior.

bun run build
bun run start

If the component no longer renders twice, the issue is development-only and caused by Strict Mode rather than a runtime bug.

2. Check your Next.js configuration

Open your Next.js config file and look for reactStrictMode.

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
};

module.exports = nextConfig;

If this is set to true, React will perform extra development checks.

3. Disable Strict Mode temporarily if you need single-render debugging

If your goal is only to stop the duplicate development render while debugging, you can turn it off temporarily.

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: false,
};

module.exports = nextConfig;

Then restart the dev server:

bun dev

This removes the extra development render check, but it also removes useful warnings. For most teams, the better approach is to keep Strict Mode on and fix code that depends on render happening only once.

4. Move side effects out of the component body

If you are doing work directly inside the render function, move it into the correct hook.

Problematic:

export default function Page() {
  fetch('/api/data');
  console.log('render');
  return <div>Page</div>;
}

Better:

import { useEffect } from 'react';

export default function Page() {
  useEffect(() => {
    fetch('/api/data');
  }, []);

  return <div>Page</div>;
}

Even then, remember that in development useEffect may still appear to run twice under Strict Mode checks. That is expected behavior for development validation.

5. Make effects idempotent

If an effect creates duplicate requests, duplicate listeners, or duplicate analytics events, make it safe to run more than once.

import { useEffect, useRef } from 'react';

export default function Page() {
  const hasTracked = useRef(false);

  useEffect(() => {
    if (hasTracked.current) return;
    hasTracked.current = true;

    console.log('track page view once');
  }, []);

  return <div>Page</div>;
}

Use this pattern carefully. It is useful for debugging or one-time client-only integrations, but the best long-term fix is writing effects that can safely mount and clean up correctly.

6. Separate expected development behavior from actual bugs

You may still have a real problem if:

  • The duplicate behavior also happens in production
  • Your API endpoint is being called twice because of both server and client fetching
  • A parent component key changes and forces a remount
  • Hot reload is re-triggering renders during editing

That is why checking both dev and production behavior is the fastest way to narrow the cause.

Common Edge Cases

Server and client both fetch the same data

In Next.js, you might fetch on the server and then trigger another fetch on the client. This looks like double rendering, but the actual issue is duplicated data fetching.

export async function getServerSideProps() {
  const data = await fetch('https://example.com/api').then(r => r.json());
  return { props: { data } };
}

If the page also calls the same API inside useEffect, you will see two requests for different reasons.

Console logs create false alarms

A simple console.log in the component body will run every render. That does not always mean the DOM changed twice. It only proves the function was invoked again.

Hot reload during development

Fast Refresh can re-run component code when files change. This can be mistaken for the same bug, especially when testing with frequent edits.

Changing keys forces remounts

If a parent passes a changing key, React treats the child as a new component and mounts it again.

<MyComponent key={Date.now()} />

This will remount every render and can look like a double-render issue even with Strict Mode disabled.

Third-party libraries with unsafe side effects

Some client-side libraries assume a component mounts exactly once. Under Strict Mode, they may register duplicate listeners or initialize twice unless wrapped carefully with cleanup logic.

FAQ

Why does Next.js render my component twice only in development?

Because React Strict Mode intentionally double-invokes component lifecycle paths in development to detect unsafe side effects. This does not usually happen in production.

Is it safe to disable reactStrictMode?

Yes, technically, but it is usually better to keep it enabled. Disabling it can hide side-effect bugs that may become harder to debug later.

How do I verify whether this is a real bug or expected behavior?

Create a production build and run it locally. If the duplicate render disappears, it is expected development behavior. If it continues, inspect fetch logic, parent keys, remounts, and client/server duplication.

For this GitHub issue, the practical conclusion is: Next.js is not unexpectedly broken here; the observed double render is the expected result of React 18 Strict Mode in development. The fix is either to keep Strict Mode and write side-effect-safe components or to temporarily disable Strict Mode if you need cleaner debugging output.

Leave a Reply

Your email address will not be published. Required fields are marked *