nextjs - 💡(How to fix) Fix Bug Report: React Compiler Only Auto-Memoizes Components on Root Page, Not on Other Routes [2 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#89006Fetched 2026-04-08 02:03:33
View on GitHub
Comments
2
Participants
3
Timeline
9
Reactions
0
Timeline (top)
labeled ×4commented ×2closed ×1issue_type_added ×1

Fix Action

Workaround

Manually wrapping components with React.memo() provides consistent behavior across all routes, but defeats the purpose of the React Compiler's automatic optimization.

Code Example

const nextConfig: NextConfig = {
  reactCompiler: true,
};

---

Operating System:
  Platform: win32
  Arch: x64
  Version: Windows 10/11

Binaries:
  Node: 20.x.x (or your version)
  npm: 10.x.x
  Yarn: N/A
  pnpm: 9.x.x

Relevant Packages:
  next: 16.1.4
  react: 19.2.3
  react-dom: 19.2.3
  babel-plugin-react-compiler: 1.0.0
  typescript: 5.x.x

Next.js Config:
  output: N/A
  reactCompiler: true

---

const nextConfig: NextConfig = {
  reactCompiler: true,
};

---

Operating System:
  Platform: win32
  Arch: x64
  Version: Windows 10/11

Binaries:
  Node: 20.x.x (or your version)
  npm: 10.x.x
  Yarn: N/A
  pnpm: 9.x.x

Relevant Packages:
  next: 16.1.4
  react: 19.2.3
  react-dom: 19.2.3
  babel-plugin-react-compiler: 1.0.0
  typescript: 5.x.x

Next.js Config:
  output: N/A
  reactCompiler: true

---

"use client";

import A from "@/src/components/test/A";
import B from "@/src/components/test/B";
import C from "@/src/components/test/C";
import { useStateContext } from "@/src/components/test/context/state-context";

export default function Home() {
  const { state, setState } = useStateContext();
  return (
    <div className='flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black'>
      Hii
      <button onClick={() => setState(state + 1)}>Increment</button>
      {state}
      <A />
      <B />
      <C />
    </div>
  );
}

---

"use client";

import A from "@/src/components/test/A";
import B from "@/src/components/test/B";
import C from "@/src/components/test/C";
import { useStateContext } from "@/src/components/test/context/state-context";

export default function page() {
  const { state, setState } = useStateContext();
  return (
    <div className='flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black'>
      Hii
      <button onClick={() => setState(state + 1)}>Increment</button>
      {state}
      <A />
      <B />
      <C />
    </div>
  );
}

---

"use client";

import A from "@/src/components/test/A";
import B from "@/src/components/test/B";
import C from "@/src/components/test/C";
import { useStateContext } from "@/src/components/test/context/state-context";

export default function page() {
  const { state, setState } = useStateContext();
  return (
    <div className='flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black'>
      Hii
      <button onClick={() => setState(state + 1)}>Increment</button>
      {state}
      <A />
      <B />
      <C />
    </div>
  );
}

---

import React from "react";

export default function A() {
  console.log("A rendered");
  return <div>A</div>;
}

---

"use client";

import { createContext, useContext, useState } from "react";

const StateContext = createContext<any>({});
export const useStateContext = () => useContext(StateContext);

export function StateProvider({ children }: { children: React.ReactNode }) {
  const [state, setState] = useState(1);
  return (
    <StateContext.Provider value={{ state, setState }}>
      {children}
    </StateContext.Provider>
  );
}

---

import { StateProvider } from "@/src/components/test/context/state-context";

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang='en'>
      <body>
        <StateProvider>{children}</StateProvider>
      </body>
    </html>
  );
}

---

A rendered  // Only on initial mount
B rendered  // Only on initial mount
C rendered  // Only on initial mount
// No additional renders when clicking increment

---

A rendered  // Initial mount
B rendered  // Initial mount
C rendered  // Initial mount
A rendered  // Click 1
B rendered  // Click 1
C rendered  // Click 1
A rendered  // Click 2
B rendered  // Click 2
C rendered  // Click 2
A rendered  // Click 3
B rendered  // Click 3
C rendered  // Click 3

---

A rendered  // Initial mount
B rendered  // Initial mount
C rendered  // Initial mount
A rendered  // Click 1
B rendered  // Click 1
C rendered  // Click 1
A rendered  // Click 2
B rendered  // Click 2
C rendered  // Click 2
A rendered  // Click 3
B rendered  // Click 3
C rendered  // Click 3
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/sanjaikumarv/next-js-compiler-issue

To Reproduce

  1. Enable React Compiler in next.config.ts:
const nextConfig: NextConfig = {
  reactCompiler: true,
};
  1. Create identical page components in multiple locations:

    • app/page.tsx (root page)
    • app/count/page.tsx (regular route)
    • app/(protected)/count/page.tsx (route group page)
  2. All pages use the same child components (A, B, C) that log when they render

  3. All pages use the same context provider for state management

  4. Click the increment button to trigger state updates on each page

Current vs. Expected behavior

Current vs. Expected behavior

Current Behavior:

  • Root page (app/page.tsx): Child components A, B, C do NOT re-render when parent state changes (React Compiler auto-memoizes them) ✅
  • Regular route (app/count/page.tsx): Child components A, B, C DO re-render on every parent state change (React Compiler does NOT auto-memoize them) ❌
  • Route group page (app/(protected)/count/page.tsx): Child components A, B, C DO re-render on every parent state change (React Compiler does NOT auto-memoize them) ❌

Expected Behavior: React Compiler should apply consistent optimization across all pages regardless of their location in the app directory structure. If it auto-memoizes components on the root page, it should do the same for all other routes (regular routes, nested routes, and route groups).

Provide environment information

Operating System:
  Platform: win32
  Arch: x64
  Version: Windows 10/11

Binaries:
  Node: 20.x.x (or your version)
  npm: 10.x.x
  Yarn: N/A
  pnpm: 9.x.x

Relevant Packages:
  next: 16.1.4
  react: 19.2.3
  react-dom: 19.2.3
  babel-plugin-react-compiler: 1.0.0
  typescript: 5.x.x

Next.js Config:
  output: N/A
  reactCompiler: true

Which area(s) are affected? (Select all that apply)

React

Which stage(s) are affected? (Select all that apply)

next dev (local), next build (local), next start (local), Vercel (Deployed)

Additional context

Bug Report: React Compiler Only Auto-Memoizes Components on Root Page, Not on Other Routes

To Reproduce

  1. Enable React Compiler in next.config.ts:
const nextConfig: NextConfig = {
  reactCompiler: true,
};
  1. Create identical page components in multiple locations:

    • app/page.tsx (root page)
    • app/count/page.tsx (regular route)
    • app/(protected)/count/page.tsx (route group page)
  2. All pages use the same child components (A, B, C) that log when they render

  3. All pages use the same context provider for state management

  4. Click the increment button to trigger state updates on each page

Current vs. Expected behavior

Current Behavior:

  • Root page (app/page.tsx): Child components A, B, C do NOT re-render when parent state changes (React Compiler auto-memoizes them) ✅
  • Regular route (app/count/page.tsx): Child components A, B, C DO re-render on every parent state change (React Compiler does NOT auto-memoize them) ❌
  • Route group page (app/(protected)/count/page.tsx): Child components A, B, C DO re-render on every parent state change (React Compiler does NOT auto-memoize them) ❌

Expected Behavior: React Compiler should apply consistent optimization across all pages regardless of their location in the app directory structure. If it auto-memoizes components on the root page, it should do the same for all other routes (regular routes, nested routes, and route groups).

Provide environment information

Operating System:
  Platform: win32
  Arch: x64
  Version: Windows 10/11

Binaries:
  Node: 20.x.x (or your version)
  npm: 10.x.x
  Yarn: N/A
  pnpm: 9.x.x

Relevant Packages:
  next: 16.1.4
  react: 19.2.3
  react-dom: 19.2.3
  babel-plugin-react-compiler: 1.0.0
  typescript: 5.x.x

Next.js Config:
  output: N/A
  reactCompiler: true

Which area(s) are affected? (Select all that apply)

  • App Router
  • React Compiler (experimental)
  • Pages Router
  • Turbopack
  • Other (please specify)

Which stage(s) are affected? (Select all that apply)

  • next dev (local)
  • next build (local)
  • next start (local)
  • Vercel (Deployed)
  • Other (please specify)

Additional context

Code Examples

Root Page (app/page.tsx) - Components DON'T re-render:

"use client";

import A from "@/src/components/test/A";
import B from "@/src/components/test/B";
import C from "@/src/components/test/C";
import { useStateContext } from "@/src/components/test/context/state-context";

export default function Home() {
  const { state, setState } = useStateContext();
  return (
    <div className='flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black'>
      Hii
      <button onClick={() => setState(state + 1)}>Increment</button>
      {state}
      <A />
      <B />
      <C />
    </div>
  );
}

Regular Route Page (app/count/page.tsx) - Components DO re-render:

"use client";

import A from "@/src/components/test/A";
import B from "@/src/components/test/B";
import C from "@/src/components/test/C";
import { useStateContext } from "@/src/components/test/context/state-context";

export default function page() {
  const { state, setState } = useStateContext();
  return (
    <div className='flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black'>
      Hii
      <button onClick={() => setState(state + 1)}>Increment</button>
      {state}
      <A />
      <B />
      <C />
    </div>
  );
}

Route Group Page (app/(protected)/count/page.tsx) - Components DO re-render:

"use client";

import A from "@/src/components/test/A";
import B from "@/src/components/test/B";
import C from "@/src/components/test/C";
import { useStateContext } from "@/src/components/test/context/state-context";

export default function page() {
  const { state, setState } = useStateContext();
  return (
    <div className='flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black'>
      Hii
      <button onClick={() => setState(state + 1)}>Increment</button>
      {state}
      <A />
      <B />
      <C />
    </div>
  );
}

Child Component Example (src/components/test/A.tsx):

import React from "react";

export default function A() {
  console.log("A rendered");
  return <div>A</div>;
}

Context Provider (src/components/test/context/state-context.tsx):

"use client";

import { createContext, useContext, useState } from "react";

const StateContext = createContext<any>({});
export const useStateContext = () => useContext(StateContext);

export function StateProvider({ children }: { children: React.ReactNode }) {
  const [state, setState] = useState(1);
  return (
    <StateContext.Provider value={{ state, setState }}>
      {children}
    </StateContext.Provider>
  );
}

Root Layout (app/layout.tsx):

import { StateProvider } from "@/src/components/test/context/state-context";

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang='en'>
      <body>
        <StateProvider>{children}</StateProvider>
      </body>
    </html>
  );
}

Console Output Comparison

Root page (/) - After clicking increment 3 times:

A rendered  // Only on initial mount
B rendered  // Only on initial mount
C rendered  // Only on initial mount
// No additional renders when clicking increment

Regular route (/count) - After clicking increment 3 times:

A rendered  // Initial mount
B rendered  // Initial mount
C rendered  // Initial mount
A rendered  // Click 1
B rendered  // Click 1
C rendered  // Click 1
A rendered  // Click 2
B rendered  // Click 2
C rendered  // Click 2
A rendered  // Click 3
B rendered  // Click 3
C rendered  // Click 3

Route group page (/(protected)/count) - After clicking increment 3 times:

A rendered  // Initial mount
B rendered  // Initial mount
C rendered  // Initial mount
A rendered  // Click 1
B rendered  // Click 1
C rendered  // Click 1
A rendered  // Click 2
B rendered  // Click 2
C rendered  // Click 2
A rendered  // Click 3
B rendered  // Click 3
C rendered  // Click 3

Impact

This inconsistency makes it difficult to:

  1. Rely on React Compiler for predictable performance optimization
  2. Understand when components will be auto-memoized
  3. Build consistent performance patterns across the application
  4. Trust the experimental React Compiler feature in production

Workaround

Manually wrapping components with React.memo() provides consistent behavior across all routes, but defeats the purpose of the React Compiler's automatic optimization.

Notes

  • All pages are identical in structure and code
  • All use the same context provider
  • All are client components ("use client")
  • The only difference is the file location: React Compiler ONLY optimizes the root page (app/page.tsx), but NOT any other routes
  • This affects both regular routes (app/count/page.tsx) and route groups (app/(protected)/count/page.tsx)
  • Disabling reactCompiler makes all pages behave consistently (all re-render all components)

extent analysis

TL;DR

The most likely fix for the inconsistent optimization issue with React Compiler is to manually wrap components with React.memo() or to explore configuration options for the React Compiler to ensure consistent behavior across all routes.

Guidance

  • Verify that the reactCompiler option is correctly enabled in next.config.ts and that the React Compiler version is up-to-date.
  • Check if there are any differences in the component code or context provider usage between the root page and other routes that could be causing the inconsistent optimization.
  • Manually wrapping components with React.memo() can provide a temporary workaround, but it may defeat the purpose of using the React Compiler for automatic optimization.
  • Explore the React Compiler documentation and configuration options to see if there are any settings that can be adjusted to ensure consistent optimization across all routes.

Example

import React from "react";

const MemoizedA = React.memo(function A() {
  console.log("A rendered");
  return <div>A</div>;
});

export default MemoizedA;

Notes

  • The issue seems to be related to the React Compiler's automatic optimization feature and its interaction with the Next.js routing system.
  • The fact that manually wrapping components with React.memo() provides consistent behavior suggests that the issue is related to the optimization logic of the React Compiler.
  • Further investigation into the React Compiler configuration options and the Next.js routing system may be necessary to find a permanent solution.

Recommendation

Apply a workaround by manually wrapping components with React.memo() until a more permanent solution can be found, as this provides consistent behavior across all routes.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING