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

[FIXED in 15.1.0] next.js 14 redirect() inside a server action can't switch between root layouts (was working in next.js 13) #58263

Closed
1 task done
meszaros-lajos-gyorgy opened this issue Nov 9, 2023 · 69 comments
Labels
locked Redirects Related to redirecting. Server Actions Related to Server Actions.

Comments

@meszaros-lajos-gyorgy
Copy link

meszaros-lajos-gyorgy commented Nov 9, 2023

FIXED IN 15.1.0!

Was fixed in this PR: #73063


Link to the code that reproduces this issue

https://github.com/meszaros-lajos-gyorgy/nextjs-14-redirect-bug

To Reproduce

  1. start the development server with npm run dev
  2. open http://localhost:3000/en in your browser
  3. Click on the "search" submit button on top of the page next to the text input (it doesn't matter what you enter into the textfield, it always redirects to the same route -> http://localhost:3000/en/search/hello)

Current vs. Expected behavior

Expected result:

You should land on /en/search/hello as that is the hardcoded redirection inside the server action in services/SiteSearch.service.ts.

( This was the behavior in Next.js 13.4.12, but it also threw an error (see #53392) )

Actual result:

The server action sends back a redirect instruction among the response headers:

X-Action-Redirect: /en/search/hello

but the page does not go to that path

Verify canary release

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

Provide environment information

Operating System:
  Platform: linux
  Arch: x64
  Version: #37~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Mon Oct  9 15:34:04 UTC 2
Binaries:
  Node: 21.1.0
  npm: 10.2.0
  Yarn: 1.22.15
  pnpm: 6.11.0
Relevant Packages:
  next: 14.0.2
  eslint-config-next: 14.0.2
  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

Additional context

When I try the same but first opening http://localhost:3000/en/search/world and then try to redirect via the server action then it works without any issues.

I've also wrote down all of the infos above to the readme in my repo.

@meszaros-lajos-gyorgy meszaros-lajos-gyorgy added the bug Issue was opened via the bug report template. label Nov 9, 2023
@meszaros-lajos-gyorgy
Copy link
Author

Still not working in 14.0.3

@meszaros-lajos-gyorgy
Copy link
Author

The issue is still present in 14.0.4-canary.18

@mathiasrscom
Copy link

mathiasrscom commented Dec 2, 2023

Do we have an update on this one?

I had to do a bit of a workaround to render my login page instead of the nav as redirect does not work for me:

// Next
import { redirect } from "next/navigation"

// Queries
import { getUser } from "@/actions/queries/user/getUser"

// Auth
import { auth } from "@/auth"

// Components
import Nav from "@/components/nav"

// Pages
import LoginPage from "../(auth)/login/page"

export default async function AppLayout({
  children,
}: {
  children: React.ReactNode
}) {
  const session = auth()

  if (!session) {
    redirect("/login")
  }

  const user = session !== null ? await getUser() : null

  return <>{user ? <Nav user={user}>{children}</Nav> : <LoginPage />}</>
}

@meszaros-lajos-gyorgy
Copy link
Author

Version 14.0.4-canary.39 still broken

@meszaros-lajos-gyorgy
Copy link
Author

meszaros-lajos-gyorgy commented Dec 4, 2023

probably the same issue or very similar: #59217

@muhaimincs
Copy link
Contributor

any work around?

@jmarbutt
Copy link

This whole time I thought it was me, but yes this is a pretty major issue for us.

@meszaros-lajos-gyorgy
Copy link
Author

Still broken in v14.0.5-canary.6.

The possible workaround here is that the server action sends back some json data to the client and then the client can do a redirect on client side. Kinda removes the purpose of having server side actions though. Or we can always downgrade back to v13 I guess.

@pinoniq
Copy link

pinoniq commented Dec 12, 2023

This actually does work for me for all redirects apart from redirect('/') or redirect('').

I have two root layouts in seperate groups. Where a server action from the login page does a redirect().

image

@charandeep7
Copy link

It's still not working

@carolinevsboliveira
Copy link

I'm with the same problem. Redirect is not updating the route

@logemann
Copy link

logemann commented Dec 25, 2023

Same for me. The first 3 hours i thought its about me being fairly new to NextJS but redirecting to some destination within my origin RootLayout works without issues. Its the context switching which breaks the redirect.

This one is pretty ugly because it affects one super mainstream use case "redirect after login", where its pretty normal that the login page is inside your "website" Routing Group and the target route is something like a dashboard in the "WebApp" Routing Group.

For the time being i will re-architect my loginpage to be a client comoonent which calls a handleSubmit() which calls my server action to auth and then get back JSON to handleSubmit where i will do a Route push. Works but ugly.

@meszaros-lajos-gyorgy
Copy link
Author

meszaros-lajos-gyorgy commented Dec 29, 2023

Off topic, but I have to admit that I regret choosing Next 13 for a new project. This whole next generation of next.js kinda feels like a work-in-progress/proof-of-concept project rather than something that is production ready.

@mr-14
Copy link

mr-14 commented Jan 5, 2024

I've spent way too many frustrating hours on this. For me, the work around is to just have a common root layout with minimum markup.

Project structure:
image

Example of /app/layout.tsx

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh">
        <body>{children}</body>
    </html>
  )
}

@meszaros-lajos-gyorgy
Copy link
Author

Still broken in v14.0.5-canary.43

@innobrightcafe
Copy link

wow, same issue. cant use with condition

@AndreyKornyusko
Copy link

still not working

@logemann
Copy link

because of that i reverted from a two root layout setup to one rootLayout (which basically has no layout at all).

@guscsales
Copy link

I've spent way too many frustrating hours on this. For me, the work around is to just have a common root layout with minimum markup.

Project structure: image

Example of /app/layout.tsx

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh">
        <body>{children}</body>
    </html>
  )
}

This works for me as well. For next@14.1.0 when you have group routes defined in the app it's required to use an minimum layout.tsx for work.

@sachinbhatnagar
Copy link

I've spent way too many frustrating hours on this. For me, the work around is to just have a common root layout with minimum markup.

Project structure: image

Example of /app/layout.tsx

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh">
        <body>{children}</body>
    </html>
  )
}

This worked! Thank you! This is such an annoying and undocumented issue. Wasted almost 3 hours before I saw this thread.

@meszaros-lajos-gyorgy
Copy link
Author

Tested it on the latest canary version 14.1.1-canary.45, still broken, updated the repo for reproducing the issue.

@iamgmd
Copy link

iamgmd commented Feb 12, 2024

Moving from NuxtJs to NextJs and got the same problem, I am starting to regret it based on this simple requirement :-(

@adamchipperfield
Copy link

How hasn't this got much attention? Feels like a pretty big issue.

@iamgmd
Copy link

iamgmd commented Feb 14, 2024

Actually, I did get this working as I wanted, bear with me, I am new to NextJs and what I am doing maybe not what is intended but for me, all of my errors have gone and the redirect works as expected along with the different layouts rendering how I want them.

In my scenario I wanted 2 layouts, the anon layout and the landing layout. The only difference between those two is that I also have a Navbar in the landing layout.

@guscsales is correct, you need the root layout and the other layouts become nested layouts (I think).

So what I did is have the layout.tsx in the root of the app, with this being the root layout, it holds all of the top level stuff. (fyi, I have ShadCn also installed) and the other layouts are in route groups (anon) and (landing)

import { Montserrat as FontSans } from "next/font/google";
import "@/styles/globals.css";

const defaultUrl = process.env.VERCEL_URL
  ? `https://${process.env.VERCEL_URL}`
  : "http://localhost:3000";

export const metadata = {
  metadataBase: new URL(defaultUrl),
  title: "My NextJs and Supabase site",
  description: "The fastest way to build apps with Next.js and Supabase",
};

import { cn } from "@/lib/utils";

export const fontSans = FontSans({
  subsets: ["latin"],
  variable: "--font-sans",
});

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body
        className={cn(
          "min-h-screen bg-background font-sans text-foreground antialiased",
          fontSans.variable,
        )}
      >
        {children}
      </body>
    </html>
  );
}

The (anon)/layout.tsx looks like this:-

export default function AnonLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="flex min-h-screen flex-col items-center">{children}</div>
  );
}

The (landing)/layout.tsx looks like this:-

import Navbar from "@/components/Navbar";

export default function LandingLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="min-h-screen bg-background font-sans text-foreground antialiased">
      <Navbar />
      {children}
    </div>
  );
}

In my (anon)/login/page.tsx I have my signin method redirect as below.

  const signIn = async (formData: FormData) => {
    "use server";

    const email = formData.get("email") as string;
    const password = formData.get("password") as string;
    const supabase = createClient();

    const { error } = await supabase.auth.signInWithPassword({
      email,
      password,
    });

    if (error) {
      return redirect("/login?message=Could not authenticate user");
    }

    return redirect("/");
  };

Apologies for the messy response, just wanted to let peeps know how I am doing this. Feel free to correct me, as I said, I am new to NextJs.

It maybe that the problem derives from the fact that you can/should only have one <html><body> so don't put them in anything else except the root layout.tsx.

@iamgmd
Copy link

iamgmd commented Feb 14, 2024

@meszaros-lajos-gyorgy I have just amended your code slightly below and the redirect works as expected:

Add layout.tsx in src/app:-

export const metadata = {
  title: "Next.js",
  description: "Generated by Next.js",
};

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

Amend src/app/[locale]/layout.tsx to:

import { SiteSearch } from "@/components/SiteSearch";

export default function LocaleLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div>
      <SiteSearch />
      {children}
    </div>
  );
}

Amend src/app/search/layout.tsx:

import { SiteSearch } from "@/components/SiteSearch";

export default function SearchLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div>
      <SiteSearch />
      {children}
    </div>
  );
}

Amend src/app/search/[search]/page.tsx to get the params:

export default async function SearchPageEN(args: any) {
  return (
    <div>
      <h1>/en/search/{args.params.search}</h1>
    </div>
  );
}

let us know if it all works for you.

@meszaros-lajos-gyorgy
Copy link
Author

meszaros-lajos-gyorgy commented Feb 15, 2024

I also ended up creating a fake root element called (root) with nothing more than the html and body tags inside. It's just inconvinient as you have to adjust the paths of every import in all the files inside the app folder.

Update: I've stopped using next.js altogether somewhere in mid-April and I'm just using vanilla react now. There are way too many unanswered questions and way too many issues which don't get addressed. In every release all I see are fixes for Turbopack.

@cantdocpp
Copy link

I do agree with comment above, that if your architecture is splitted between route groups without any root layout, then you need to have 1 root layout on your project that's used by your next app.

I think it's a bug that next cannot redirect to different layout

@dorji-dev
Copy link

Still not working. I have two root layouts for the info. Next version is 14.1.0. As a workaround for now, I am returning a status from the server action like {status: error | success} and based on that handling the redirection on the client side via useRouter from next/navigation.

@arkantoster

This comment was marked as resolved.

@alexhwoods
Copy link

in my case, when I put it inside of a try-catch block it is not working, but when I remove it it is working fine

Hey @RuentDev, this is because redirect throws a Next.js specific error. It's kind of part of the "rules of redirects" that you can't put them in a try catch. (See here)

@VesperQuartz
Copy link

I've spent way too many frustrating hours on this. For me, the work around is to just have a common root layout with minimum markup.
Project structure: image
Example of /app/layout.tsx

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh">
        <body>{children}</body>
    </html>
  )
}

This works for me as well. For next@14.1.0 when you have group routes defined in the app it's required to use an minimum layout.tsx for work.

Wow this solved my issue, at least they should have mentioned this in the Documentation

@VesperQuartz
Copy link

it seems weird that the documentation explicitly says that if you're going to use route groups to have multiple root layouts you should remove the top-level layout.js file but some people here say that adding it is actually what make the redirect works so disappointing.

Yeah, it was cause of what the doc said that made me remove it, now i added it back and everything works fine

@danesto
Copy link

danesto commented Jul 25, 2024

Confirming this is still an issue with latest version of Next. Redirect between multiple root layouts without main root layout inside the app dir does not work.

@vmesel
Copy link

vmesel commented Jul 26, 2024

@danesto yep, same error here!

@danesto
Copy link

danesto commented Jul 26, 2024 via email

@Blakew18
Copy link

I've spent way too many frustrating hours on this. For me, the work around is to just have a common root layout with minimum markup.

Project structure: image

Example of /app/layout.tsx

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh">
        <body>{children}</body>
    </html>
  )
}

This just saved me from days of heart ache. Bless you good sir

@luisdil
Copy link

luisdil commented Aug 13, 2024

The error seems to persists on 14.2.5 and even on 15 RC. This is really unfortunate since we need the functionality for multiple root layouts and seamless redirections. Is this something the Next team is actively working on?

@sslexus
Copy link

sslexus commented Sep 15, 2024

What a pain in the ass if a simple redirect function doesn't even work properly.

All I want to do is this

export async function Redirect() {
  redirect("/dashboard");
  return null;
}

This is literally as simple as it gets, and I am met with this error:
Screenshot 2024-09-15 at 11 08 42

Console error: Uncaught Error: Rendered more hooks than during the previous render.

Dear NextJS team: this is an extremely basic functionality, yet you guys are working on such useless "fancy" features like your cute parallel route or "vercel AI". Fix this basic stuff then go with those fancy, gimmicky BS. @leerob

@lgmarenco1987
Copy link

I can confirm that using a layout file outside the grouping works, but just to be careful of using only the html tag in order to avoid any tags repeated and some class error in the console:
Project Structure:
image
MainLayout:
export default async function MainLayout({ children, }: { children: React.ReactNode; }) { return <html lang="en">{children}</html>; }
As everyone had suggested, I think this way more complicated that it should be, but good that I found this thread (It took me 6 hours to fix haha)

@Nivg
Copy link

Nivg commented Oct 9, 2024

The minimal root layout approach does work, but if you refresh your page you can see that the other layouts are basically falls back to the root layout, meaning that the root layout should have the html and body tags, but the other layouts shouldn't.

This is not documented, and if that not a bug, it should be stated by the Next.js team.

@holem
Copy link

holem commented Oct 28, 2024

Same behavior with 15.0.1
People here right, such a basic stuff must just work or fixed urgently. But this bug lives on almost a year and it's not even in progress of fixing, it's unbelievable. I have started to doubt whether it was a right choice for a new project.

@daraHK88
Copy link

inside the app folder you must have a layout.tsx file with this content so redirect function inside server actions will work

export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body>{children}</body> </html> ) }

@Blakew18
Copy link

Blakew18 commented Oct 30, 2024

inside the app folder you must have a layout.tsx file with this content so redirect function inside server actions will work

`export default function RootLayout({ children }: { children: React.ReactNode }) {

return (

    <html lang="en">

    <body>{children}</body>

    </html>

)

}`

While this is the work around and fixes the issue. The docs clearly state that we should not be doing this.

When using route groups the next docs clearly state

"To create multiple root layouts, remove the top-level layout.js file, and add a layout.js file inside each route group."

So they either need to fix the documentation or fix the functionality.

Ideally fix the functionality. Or at a minimum update the docs and reflect it as a breaking change from older versions.

@stefanedberg
Copy link

I'm having this issue as well. Currently using the app/layout.tsx workaround.

@sorenhoyer
Copy link

Any update on this? This is a pretty serious bug that is wasting a lot of peoples time before arriving here and realizing it's not a problem in your own code but a bug in the framework.

@catlilface
Copy link

Still not working on 15.0.3

@anshul360
Copy link

Is it documented somewhere to have common root layout for redirect to work? wasted >4hrs just to realize there is some issue with root layout.

marksamfd added a commit to Anas-Ah25/Cakery that referenced this issue Dec 13, 2024
- redirection is fixed using client side redirection due to a known issue in Nextjs 14 vercel/next.js#58263
fixes Fix Redirection after login #62
@meszaros-lajos-gyorgy
Copy link
Author

meszaros-lajos-gyorgy commented Dec 18, 2024

13 months have passed since I've opened the issue, it's clear that vercel doesn't give a damn

@philwolstenholme
Copy link
Contributor

I noticed #73063 in a recent changelog.

If you're affected by this issue you can help everyone out by reporting if it is fixed in the latest canary release.

@meszaros-lajos-gyorgy
Copy link
Author

I noticed #73063 in a recent changelog.

If you're affected by this issue you can help everyone out by reporting if it is fixed in the latest canary release.

You are a life saver, I haven't tracked all the changes that have been happening. Retested it and it works in 15.1.0 stable release which came out last week!

@meszaros-lajos-gyorgy meszaros-lajos-gyorgy changed the title next.js 14 redirect() inside a server action can't switch between root layouts (was working in next.js 13) [FIXED in 15.1.0] next.js 14 redirect() inside a server action can't switch between root layouts (was working in next.js 13) Dec 18, 2024
Copy link
Contributor

github-actions bot commented Jan 2, 2025

This closed issue has been automatically locked because it had no new activity for 2 weeks. 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 added the locked label Jan 2, 2025
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 2, 2025
@samcx samcx added Server Actions Related to Server Actions. Redirects Related to redirecting. and removed bug Issue was opened via the bug report template. labels Jan 29, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
locked Redirects Related to redirecting. Server Actions Related to Server Actions.
Projects
None yet
Development

No branches or pull requests