next.js
next.js copied to clipboard
calling `redirect()` inside a server component causes flashing UI
Link to the code that reproduces this issue
https://github.com/Fredkiss3/bug-next-redirect-flashing-ui
To Reproduce
- click on the link
- you will see a blank page while the page is being redirected
Current vs. Expected behavior
You can see in the video below that for server actions it works as expected, the page don't flash while being redirected (this is the desired behavior), while for link it doesn't :
https://github.com/vercel/next.js/assets/38298743/415037db-31a9-4cde-9b13-4f7c6eb99c3a
Verify canary release
- [X] 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:43:05 PDT 2023; root:xnu-10002.1.13~1/RELEASE_ARM64_T6020
Binaries:
Node: 18.17.1
npm: 9.6.7
Yarn: N/A
pnpm: N/A
Relevant Packages:
next: 13.5.5
eslint-config-next: 13.4.6
react: 18.2.0
react-dom: 18.2.0
typescript: 4.9.5
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
App Router
Additional context
No response
Having the same issue here. I am using redirect to add a missing query parameter or replace it if it's invalid. And I have a loading.tsx. When I navigate directly to a URL that doesn't require any redirects the loading is seamlessly replaced by the rendered page. But when there's a redirect, it flashes a white screen between the loading content and the rendered page. It should keep displaying the loading until the rendered page is ready even if there's a redirect.
I also have this issue 😢
I am digging into this. When clicking on the link there are 2 fetches being made:
The first one returns just what seems to be the empty page:
0:["development",[["children","redirect",["redirect",{"children":["source",{"children":["__PAGE__",{}]}]}],null,null]]]
And the second one returns the whole thing.
The only difference between the 2 are the headers. The first one sets Next-Router-Prefetch: 1 and the second one doesn't.
I think this might be related to the issue. I believe the first call should return the whole thing in the first place.
I have intercepted the requests and removed that header and now I only see 1 request, but the issue persists :(
const originalFetch = window.fetch;
// Override the global fetch function
window.fetch = async (input, init = {}) => {
// Ensure the init object and its headers property are properly set up
init.headers = new Headers(init.headers);
// Check and remove the 'Next-Router-Prefetch' header if present
if (init.headers.has('Next-Router-Prefetch')) {
init.headers.delete('Next-Router-Prefetch');
}
// Call the original fetch function with the modified init object
return originalFetch(input, init);
};
As a workaround, use a standard a tag to render the link instead of next/link. It will default to standard browser navigation with no flashing.
@4lejandrito thanks for looking into this. For me, unfortunately, the workaround won't work. I have a server component that does a redirect based on some logic and I see the flash even if I type the URL into the browser address bar and navigate directly. For now, I just minimized the number of cases when redirect happens by eagerly putting together the correct URL where the page is linked so redirect won't even be necessary. This at least prevents flashing when the user navigates through the links on the UI.
I am doing the same, reducing the amount of redirects. For me the most common scenario for this issue is the login redirect. I have server components that require users to be logged in and if not redirect them to the login page. All those cause this flashing. Some of them I could turn into standard as but others I could not because I would lose some UI state that I want to keep between navigations.
I'm experiencing the same issue, I found that this bug was fixed for server actions here https://github.com/vercel/next.js/issues/49424, but not for Server Component's redirect.
@balazsorban44 is it possible to have that fixed for Server Components also and render loading instead of null while redirect is executed as part of the condition inside the server component?
This PR seems to solve it: https://github.com/vercel/next.js/pull/49439
@kasperpeulen
That PR was closed a year ago
@timneutkens Sorry for pinging you, but you mentioned in #49424 that specifically this feature is needed: https://github.com/facebook/react/pull/26854, which has been on idle for over than one year. Are we still waiting for this to be merged or is there something else planned moving forward? Is there an update on this? As far as I can see this is still a problem for v15.
I have the same issue. I am loading some data in a layout component and then redirecting to a nested route if the data is not empty.
Eg. accounts/[accountId]/[bankId]
Each page is dependent on the data from its parent, but accountId or bankId could be empty based on the user. The loading screen shows but once the redirect is called, the screen is blank until the nested route is loaded.
Current strategy is to try preload the data and directly route to the nested route, avoiding a possible redirect.
To confirm, also experiencing this.
Redirecting users when they land on a /pricing/page.tsx if they already have existing data - redirecting them to the relevant /pricing/[orgId]/page.tsx - however in that time whilst redirecting, there's a flash: where nothing inside of the /pricing/layout.tsx appears.
It would be great for it to be able to return to the loading.tsx/suspense boundary.
Has anyone got a nice solution for this at the minute?
EDIT: Not ideal. But for now, have made a separate client component that takes in the [orgId] and uses useRouter and I push to the page after the client-side renders within a useEffect: router.push(/pricing/${organisation.uuid}); instead of redirect - this does prevent the flash - but does mean it doesn't happen as early as the server-side redirect would
Can confirm this is still happening on latest next.js canary
Edit by maintainer bot: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote 👍 on the issue description or subscribe to the issue for updates. Thanks!
I guess current solution is to not use redirect function from NextJS and instead do our own useRouter redirection and pass the URL to page, so we can show nie UI with it. Hopefully this can be fixed soon.
I've encountered this exact same problem, being on /my-account page and user signs out a blank page (with just the root layout) is shown while redirect is taking place, my best workaround for this moment, was to pass the path the user is on to my signout server action and check there against protected routes and redirect if user is on a protected route.
It's worse... it errors with client-side exception sadly. Due to: https://github.com/vercel/next.js/discussions/59493
Same behavior for me. I have a server component that fetches some data from an API and determines whether to perform a permanentRedirect() based on the response. What happens then is I get a 200 OK on the initial document and my full layout is briefly rendered before the real redirect is performed.
Just experienced this behavior
Same here - happening on latest canary 15.4.0-canary.26
same problem
Edit by maintainer bot: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote 👍 on the issue description or subscribe to the issue for updates. Thanks!
I also have this problem, except that I have a root layout.tsx which is rendered between the two pages for a short amount of time. For me it's just a header and a footer with an empty children which looks ugly and shouldn't happen.
Put a spinner and render children on a template.tsx instead of a loading.tsx, works for my server page that always redirects.
For future references, in nextjs 15.5+ you can use middleware.ts to avoid this. Example for nodejs runtime:
import { NextRequest, NextResponse } from "next/server";
export async function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith("/redirect-from-this-url")) {
// some auth check here or something
const url = new URL("/", request.url);
url.searchParams.set("message", "login-required");
return NextResponse.redirect(url);
}
return NextResponse.next();
}
export const config = {
matcher: ["/redirect-from-this-url"],
runtime: "nodejs",
};
For future references, in nextjs 15.5+ you can use middleware.ts to avoid this. Example for nodejs runtime:
import { NextRequest, NextResponse } from "next/server"; export async function middleware(request: NextRequest) { if (request.nextUrl.pathname.startsWith("/redirect-from-this-url")) { // some auth check here or something const url = new URL("/", request.url); url.searchParams.set("message", "login-required"); return NextResponse.redirect(url); } return NextResponse.next(); } export const config = { matcher: ["/redirect-from-this-url"], runtime: "nodejs", };
This would work buut if you're doing database look ups to direct user to the right path, it'll cause multiple database lookups for the same data ( middleware and server component ). No way to share data directly between the two. We could cache that data but now we have to deal with caching which would cause it's own set of problems.
It would be great to atleast show the loader.tsx while the redirect is happening so it doesn't UI flashes between root layout and loader.tsx.
Also experience this
Is there a solution now?
Edit by maintainer bot: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote 👍 on the issue description or subscribe to the issue for updates. Thanks!