next.js: `redirect` on the server (server action) causes page double render

Link to the code that reproduces this issue

https://github.com/benoitgrelard/next-redirect

To Reproduce

  1. Start the application in dev mode (npm run dev)
  2. Redirect to the /email page using the different ways provided on the page and note the logs in the terminal (server side)

Current vs. Expected behavior

Current:

  1. Calling redirect inside the server action seems to cause a double render (you see a double log)
  2. Calling redirect on the client only causes one render (although I sometimes also see 0 logs… 🤔)
  3. Clicking on the link to the /email page only causes one render (you see one log)
  4. Loading/re-loading the /email page directly only shows one log

Another thing I’ve noticed that I don’t quite understand: Once on the /email page, and using the back button to browse back history:

  • using the redirect on the client button won’t log anything (no render?)
  • using the link won’t log anything either
  • using redirect on the server always still logs twice

Expected:

  • Considering the /email page is a React server component, I expect it to only be rendered once.

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 23.0.0: Fri Sep 15 14:41:43 PDT 2023; root:xnu-10002.1.13~1/RELEASE_ARM64_T6000
Binaries:
  Node: 18.16.1
  npm: 9.5.1
  Yarn: 1.22.18
  pnpm: N/A
Relevant Packages:
  next: 13.5.6
  eslint-config-next: 13.5.6
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.2.2
Next.js Config:
  output: N/A

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

App Router, Routing (next/router, next/navigation, next/link)

Additional context

No response

About this issue

  • Original URL
  • State: closed
  • Created 8 months ago
  • Reactions: 30
  • Comments: 25 (1 by maintainers)

Most upvoted comments

Yes, looks like this was resolved in #63786, which is available as of v14.2.0-canary.48.

I cloned the original repro and it appears to work as expected, so I’m going to close this out. Happy to investigate if people are still running into this behavior!

https://github.com/vercel/next.js/assets/1939140/8e2fecaf-a98e-4b68-8f8f-b6b52b9304c4

I am also having the same issue on 14.1.0. On my project I have a server rendered home page on / that checks some details on the database and then redirects the user to one of two pages. Both pages have their own async fetches on the server side, and result in the double render as described.

My workaround is to perform the initial redirect on the client side in a side effect. For example:

page.tsx

import HomeRedirect from "./HomeRedirect";

export default async function Home() {
  return <HomeRedirect />;
}

HomeRedirect.tsx

"use client";
import { redirect, useRouter } from "next/navigation";

export default function HomeRedirect() {
  const router = useRouter();

  useEffect(() => {
    redirect("/other-page");
  }, [router]);

  return null;
}

With this I see a flash of the loading component, but the second page is rendered only once so it looks much less ugly.

My only other workaround would be to move all data fetching to the client side of the pages being redirected to. But this would be a huge hassle and defeat the purpose of server components.

A few months ago I had to downgrade because of a critical bug (#56018). Now I want to update to a newer version, but I find this redirect thing… Lately, Next.js has been surprising me with the number of bugs and how long it takes to fix them 🙁

Stumbled upon this issue after seeing the same thing happen with a very basic redirect on a new NextJS 14 app. Also pretty surprising that this happens, considering how noticeable it is in production.

FWIW I ended up going with the workaround mentioned by @robbiemccorkell above (thanks!)

Nope, no work around yet, just randomly renders twice after a server side redirect no matter what.

My use case: I’ve some app pages which might do a server-side redirect depending of the result of an GET request (server-side) on that page. The redirect works and then triggers a re-render at the resulting / redirected page, causing a beautiful UI flickering / re-render.

Basically @wingy3181 already has a nice basic repro posted.

I just reproduced it using @wingy3181s repo and upgraded to Next v14.1.0. Super weird this issue remains! Seems its a very fundamental issue to Next. I love the framework, that’s why it’s so baffling to me this issue isn’t prioritised or even addressed! I have multiple server components that trigger some important API code when mounted. That code is obviously run twice, and I end up with duplicated data.

I can also confirm this issue seems to exist also on non-serveraction redirects to a server component.

Thanks for all the repos reproducing this very clearly.

This is still not fixed in 14.2.1… The component that the user is being redirected to is again, in fact, rendered twice.

Just checked, bug still exists even in latest 14.1.0

Could this issue be related to this https://github.com/vercel/next.js/pull/62561?

Have the same issue

"use server"

import { getToken } from "@/lib/auth"
import { revalidatePath } from "next/cache"
import { redirect } from "next/navigation"

const url = `${process.env.AUTH_PUBLIC_BAKEND_URL}/api/users/clients`

function checkAddress(formData) {
  const address = formData.get("address")
  const town = formData.get("town")
  const zip_code = formData.get("zip_code")
  const province = formData.get("province")
  const country = formData.get("country")

  const isAddressEmpty = !address && !town && !zip_code && !province && !country

  if (!isAddressEmpty) {
    return {
      address,
      town,
      zip_code,
      province,
      country,
    }
  }
}

export async function createClient(prevState, formData) {
  const token = await getToken()

  const clientData = {
    client: {
      name: formData.get("name"),
      first_name: formData.get("first_name"),
      last_name: formData.get("last_name"),
      email: formData.get("email"),
      phone: formData.get("phone"),
      tags_names: formData.getAll("tags_names"),
    },
  }

  if (checkAddress(formData)) {
    clientData.client.addresses_attributes = [checkAddress(formData)]
  }

  const response = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json",
      Authorization: token,
    },
    body: JSON.stringify(clientData),
  })

  const responseBody = await response.json()

  if (responseBody.status == "created") {
    revalidatePath("/dashboard/usuarios/clientes")
    redirect(`/dashboard/usuarios/clientes/${responseBody.data.id}`)
  } else {
    return responseBody.errors
  }
}

Double Get caused by redirect image

@smozely It works for me. I use cache fetch to call external api. Even if it’s going to call “next server” twice, but my spring backend doesn’t have to react to this call because data cache in next server will do the job.

EDIT: didn’t see there was already a repository with example…here is another example i guess

Reproduced as well for me Have created example repository with the issue at https://github.com/wingy3181/redirect-double-render-server-component-example

Navigate to http://localhost:3000/from and click on the button “Redirect to”

I see the following log from the server

> redirect-double-render-server-component-example@0.1.0 dev
> next dev

   ▲ Next.js 14.0.1
   - Local:        http://localhost:3000

 ✓ Ready in 13.7s
 ○ Compiling /from/page ...
 ✓ Compiled /from/page in 7.2s (479 modules)
FromPage
 ✓ Compiled in 1264ms (218 modules)
 ○ Compiling /favicon.ico/route ...
 ✓ Compiled /to/page in 2.9s (486 modules)
ToPage

FromPage
handleAction
ToPage
ToPage
^C