Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEXT-1220] layout.js rendering twice in production #49115

Closed
1 task done
joshwcomeau opened this issue May 2, 2023 · 11 comments · Fixed by #52589
Closed
1 task done

[NEXT-1220] layout.js rendering twice in production #49115

joshwcomeau opened this issue May 2, 2023 · 11 comments · Fixed by #52589
Labels
bug Issue was opened via the bug report template. linear: next Confirmed issue that is tracked by the Next.js team. locked

Comments

@joshwcomeau
Copy link

joshwcomeau commented May 2, 2023

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
      Platform: darwin
      Arch: arm64
      Version: Darwin Kernel Version 22.4.0: Mon Mar  6 20:59:28 PST 2023; root:xnu-8796.101.5~3/RELEASE_ARM64_T6000
    Binaries:
      Node: 16.17.0
      npm: 8.15.0
      Yarn: 1.22.19
      pnpm: N/A
    Relevant packages:
      next: 13.3.5-canary.3
      eslint-config-next: 13.3.4
      react: 18.2.0
      react-dom: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

App directory (appDir: true)

Link to the code that reproduces this issue

https://codesandbox.io/p/sandbox/layout-render-twice-u24kxo

To Reproduce

  • Visit the CodeSandbox link
  • Open the "dev:logs" pane
  • Notice that duplicate logs occur:

CleanShot 2023-05-02 at 18 14 55@2x

Describe the Bug

The root layout.js will render twice on the server for every request, when using dynamic routing (const dynamic = 'force-dynamic';). This happens even though it's a Server Component (no "use client" directive).

My first thought was that this was Strict Mode related, but the double-render occurs even when disabling Strict Mode in next.config.js, as well as when serving a production build.

This is happening in the root layout.js, as well as any components rendered within layout.js. It does not seem to be happening in page.js.

Expected Behavior

Each request should generate a single render, so that any expensive operations (eg. database calls) only fire once.

EDIT: with further testing, I notice that these two renders are done in parallel, so it wouldn't actually slow the response, but it does cause problems if there are side effects (eg. a hit counter that registers every render).

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

NEXT-1220

@joshwcomeau joshwcomeau added the bug Issue was opened via the bug report template. label May 2, 2023
@timneutkens
Copy link
Member

Hey Josh, I can't open the sandbox, happy to take a look. Without looking at the code I'm assuming what you're seeing is the preloading behavior, which will only end up with a single render but it's not guaranteed that React only ever renders once anyway because of Suspense boundaries.

Each request should generate a single render, so that any expensive operations (eg. database calls) only fire once.

All expensive operations should leverage import { cache } from 'react' so that they only ever happen once per request instead of once per call, that way rerendering because of e.g. a Suspense boundary resolving would cause a similar second render.

@joshwcomeau
Copy link
Author

joshwcomeau commented May 3, 2023

Hi @timneutkens! Ahh that's super helpful, thanks for the context!

In case it's useful, here's the code from that codesandbox, in the root layout.js:

export default function RootLayout({ children }) {
  console.log("Render", Date.now());
  
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

I see two logs firing 1-2 milliseconds apart when refreshing the page.

RE: preloading behavior, do you happen to know where I can learn more about it? Tried googling but I couldn't find anything. Although I realize all this stuff is in beta / docs might not exist yet.

@choisuill
Copy link

choisuill commented May 16, 2023

I have same issue
Why is it rendered twice?

// app / layout.tsx
export default async function RootLayout({ children }: IProps) {
  console.log('RootLayout Render');
  return (
    <html lang="en">
      <body>
         <main>{children}</main>
      </body>
    </html>
  );
}`

// app / page.tsx
export default async function Home() {
  console.log('Home Render');
  return <div>Home</div>
}
result
Home Render
RootLayout Render
RootLayout Render

@mohamadfala
Copy link

I have same issue Why is it rendered twice?

// app / layout.tsx
export default async function RootLayout({ children }: IProps) {
  console.log('RootLayout Render');
  return (
    <html lang="en">
      <body>
         <main>{children}</main>
      </body>
    </html>
  );
}`
// app / page.tsx
export default async function Home() {
  console.log('Home Render');
  return <div>Home</div>
}
result
Home Render
RootLayout Render
RootLayout Render

I'm facing the exact same issue! i tried removing everything and kept it as basic code exactly as you provided, but it keeps rendering twice (not because of strict mode, i have turned this off already).

I'm using next-auth, but i did remove the session provider and everything else that could affect this behavior.

any luck finding a resolution to your issue?

@mohamadfala
Copy link

@joshwcomeau I was wondering if you did find your issue.. 👀

@timneutkens timneutkens added the linear: next Confirmed issue that is tracked by the Next.js team. label May 22, 2023
@timneutkens timneutkens changed the title layout.js rendering twice in production [NEXT-1220] layout.js rendering twice in production May 22, 2023
@timneutkens
Copy link
Member

This is caused by the root not-found boundary being applied by default, so it renders the layout around the not-found boundary in order for it to render correctly when notFound() is called in the root layout under certain conditions. This can be improved in the future so I've synced it into our issue tracker.

@0xAnakin
Copy link

0xAnakin commented Jun 7, 2023

Any updates on this?

@Stewcha

This comment was marked as spam.

shuding pushed a commit that referenced this issue Jul 13, 2023
Introduce a new way to search for `not-found` component that based on
the request pathname and current loader tree of that route. And we
search the proper not-found in the finall catch closure of app
rendering, so that we don't have to pass down the root layout to
app-router to create the extra error boundary.

This ensures the root layout doesn't have duplicated rendering for
normal requests

Fixes NEXT-1220
Fixes #49115
@karlhorky
Copy link
Contributor

I think the PR that fixed this (#52589) may have removed the layout in our not-found.tsx page:

Copying my comment over here if it's helpful for anyone:


Hmm... wonder if this is causing the layout to be missing on the not-found.tsx page:

Before (next@13.4.9, left navigation from layout):

not-found

After (next@13.4.10, no left navigation from layout):

not-found

@karlhorky
Copy link
Contributor

Opened an issue to report the problem of not-found losing the layout here:

@github-actions
Copy link
Contributor

This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Issue was opened via the bug report template. linear: next Confirmed issue that is tracked by the Next.js team. locked
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants