What is the _rsc request in NextJS and why are there multiple requests when clicking on a <Link>?
Unanswered
West African Lion posted this in #help-forum
West African LionOP
While writing code utilizing the parallel routes of NextJS,
I realized that
The backend server's api is being called 6 times.
All of the API fetch logic for many slots is being called 6 times.
The middleware is being called 6 times.
Clicking on the <Link> component in the browser generates six http requests with the query parameter _rsc.
I didn't find any more top-level reasons why this is happening, so what am I facing and how can I reduce the number of API calls?
I realized that
The backend server's api is being called 6 times.
All of the API fetch logic for many slots is being called 6 times.
The middleware is being called 6 times.
Clicking on the <Link> component in the browser generates six http requests with the query parameter _rsc.
I didn't find any more top-level reasons why this is happening, so what am I facing and how can I reduce the number of API calls?
44 Replies
@West African Lion While writing code utilizing the parallel routes of NextJS,
I realized that
The backend server's api is being called 6 times.
All of the API fetch logic for many slots is being called 6 times.
The middleware is being called 6 times.
Clicking on the <Link> component in the browser generates six http requests with the query parameter _rsc.
I didn't find any more top-level reasons why this is happening, so what am I facing and how can I reduce the number of API calls?
By default, next prefetch all the <Link /> on the viewport in production which make a request to the route with ?_rsc=xxx for faster navigation.
It can be disabled by setting prefetch props on <Link /> to false.
It can be disabled by setting prefetch props on <Link /> to false.
@Ray By default, next prefetch all the <Link /> on the viewport in production which make a request to the route with ?_rsc=xxx for faster navigation.
It can be disabled by setting prefetch props on <Link /> to false.
West African LionOP
If so, does <Link> generate as many requests as there are slots in the parallel routes?
not sure what you mean but it will prefetch all the url within the viewport
btw, do you use
cache() from react to dedup the request?West African LionOP
Here's a snippet of my code.
- src/app/nft/home/@newNFT/pages.tsx
- src/lib/getQueryClient.ts
- src/app/nft/home/@newNFT/pages.tsx
import { dehydrate, HydrationBoundary } from '@tanstack/react-query';
import { getNFTs } from '@/lib/apis/nft';
import NewNFT from '@/components/nft/organisms/NewNFT';
import getQueryClient from '@/lib/getQueryClient';
const NewNFTServerComponent = async () => {
const queryClient = getQueryClient();
await queryClient.prefetchQuery({
queryKey: getNFTs({ ordering: '-created', page_size: '4' }).queryKey,
queryFn: getNFTs({ ordering: '-created', page_size: '4' }).fetch,
});
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<NewNFT />
</HydrationBoundary>
);
};
export default NewNFTServerComponent;- src/lib/getQueryClient.ts
import { QueryClient } from '@tanstack/react-query';
import { cache } from 'react';
const getQueryClient = cache(() => {
return new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
},
},
});
});
export default getQueryClient;The browser's logs show that the api fetch logic of the NewNFTServerComponent is executed six times with six requests
and you are using parallel routes on that page?
West African LionOP
That's right, I'm using a total of 5 slots.
- src/app/nft/home/layout.tsx
- src/app/nft/home/layout.tsx
import MainTemplate from '@/components/nft/templates/MainTemplate';
import React from 'react';
interface Props {
topNFT: React.ReactNode;
likeNFT: React.ReactNode;
newNFT: React.ReactNode;
issue: React.ReactNode;
articleBar: React.ReactNode;
}
const Layout = async ({
topNFT,
likeNFT,
newNFT,
issue,
articleBar,
}: Props) => {
return (
<MainTemplate
topNFT={topNFT}
likeNFT={likeNFT}
newNFT={newNFT}
issue={issue}
articleBar={articleBar}
/>
);
};
export default Layout;West African LionOP
I tried alternating between true and false values for queryClient on the client side and server side, but to no avail.
@West African Lion I tried alternating between true and false values for queryClient on the client side and server side, but to no avail.
could you try not to prefetch on server and see if it still make 6 request?
West African LionOP
Thank you. Is it magic?
West African LionOP
Oh, I guess I checked wrong, it's still the same thing
West African LionOP
As a test, I increased the number of slots in the parallel routes by one, and it increased the number of requests by one.
what version of next are you using? I only get one request on a parallel routes
West African LionOP
Operating System:
Platform: darwin
Arch: x64
Version: Darwin Kernel Version 23.2.0: Wed Nov 15 21:54:10 PST 2023; root:xnu-10002.61.3~2/RELEASE_X86_64
Binaries:
Node: 20.5.1
npm: 9.8.0
Yarn: 4.0.2
pnpm: N/A
Relevant Packages:
next: 14.0.4
eslint-config-next: 14.0.3
react: 18.2.0
react-dom: 18.2.0
typescript: 5.3.2
Next.js Config:
output: standalonesame version as me but I'm not using react-query. I'm wondering is it something caused by
HydrationBoundaryalso, could you try without middleware?
West African LionOP
Yes, duplicate _rsc requests occurred even without middleware
West African LionOP
I created an example github respository (https://github.com/SeokJunYeom/nextjs-prefetch-test). It seems to be a next.js-specific issue as it is reproduced even without the @tanstack/query library, and I wonder if it is related to the Router Cache in the link below as I saw a prefetch happening every 30 seconds. https://nextjs.org/docs/app/building-your-application/caching#router-cache
yeah I got duplicated request after 30 sec but it only happen in dev
it doesn't happen in production
@West African Lion The browser's logs show that the api fetch logic of the NewNFTServerComponent is executed six times with six requests
from the screenshot here, I think you were trying it on dev mode? Because in production, the initiator are some random id
West African LionOP
It’s screenshot of production.
West African LionOP
I also found a prefetch request that was not making duplicate requests, the difference being that it had the
Next-Router-Prefetch header value.West African LionOP
As you said, the above repository was working fine in production. I struggled for a while to reproduce the issue in the above repository in my project and finally realized that it was caused by running the headers() function in the Root Layout. I have updated the code in the above repository, could you please take a look at it?
West African LionOP
I have three guesses
1. it looks like a page tree or something is being formed at build time to create a list of things to prefetch when the
2. functions that use
3. rsc request seems to occur when the cache value of
1. it looks like a page tree or something is being formed at build time to create a list of things to prefetch when the
<Link> component is clicked.2. functions that use
requestAsyncStorage, like headers() and cookies(), seem to be causing rsc requests.3. rsc request seems to occur when the cache value of
fetch() is no-store.@West African Lion I have three guesses
1. it looks like a page tree or something is being formed at build time to create a list of things to prefetch when the `<Link>` component is clicked.
2. functions that use `requestAsyncStorage`, like `headers()` and `cookies()`, seem to be causing rsc requests.
3. rsc request seems to occur when the cache value of `fetch()` is `no-store`.
it only happen on dynamic page. The reason why your previous repo doesn't occur this problem is, the page are static generated. And using
Since the page is a dynamic page and when you navigate it, it will refetch the data. Also, it is a parallel routes, so there are multiple request made.
However, the problem is the router cache seem broken after the first 30 second pass. It refetch the route on every navigation.
According to the doc, we can pass
Could you try it on your side with prefetch=true?
headers() or cookies() will make the page become dynamic or you could try to put export const dynamic = 'force-dynamic' to root layout. It should make all page dynamicSince the page is a dynamic page and when you navigate it, it will refetch the data. Also, it is a parallel routes, so there are multiple request made.
However, the problem is the router cache seem broken after the first 30 second pass. It refetch the route on every navigation.
According to the doc, we can pass
prefetch={true} to the <Link /> component to make the router cache for the route caching for 5 mins so I try it. And look like the router cache works as intended with prefetch trueCould you try it on your side with prefetch=true?
West African LionOP
Thank you! It solved my problem and gave me a great idea for digging into the nextjs internal source code, especially the prefetch behavior based on the navigation reducer source code.
@West African Lion Thank you! It solved my problem and gave me a great idea for digging into the nextjs internal source code, especially the prefetch behavior based on the navigation reducer source code.
Not sure is it a bug or what, the behavior of the router cache is different between 30s vs 5min
@Ray Not sure is it a bug or what, the behavior of the router cache is different between 30s vs 5min
West African LionOP
I checked the source code.
https://github.com/vercel/next.js/blob/43410c992684670bc95dad25873f1ec89fed2f25/packages/next/src/client/components/router-reducer/get-prefetch-cache-entry-status.ts
In another file, I found that when
I realized that it is the
https://github.com/vercel/next.js/discussions/60279
It's sad that there's no response
https://github.com/vercel/next.js/blob/43410c992684670bc95dad25873f1ec89fed2f25/packages/next/src/client/components/router-reducer/get-prefetch-cache-entry-status.ts
In another file, I found that when
NODE_ENV === 'developement', the value of kind becomes stale, and when it is stale, it is subject to refetch, and when <Link prefetch={true}>, the value of kind becomes full and it becomes reusable between 30 seconds and 5 minutes.I realized that it is the
stale behavior that is problematic and cannot be avoided in the development environment, so I created a discussion on github.https://github.com/vercel/next.js/discussions/60279
It's sad that there's no response

I think there should be something wrong with the router cache
West African LionOP
I don't understand the refetch behavior of router cache. The code is [creating a list of segments based on parallel routes](https://github.com/vercel/next.js/blob/43410c992684670bc95dad25873f1ec89fed2f25/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts#L51) and [traversing them](https://github.com/vercel/next.js/blob/43410c992684670bc95dad25873f1ec89fed2f25/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts#L77), [filling the fetch response with data values that are lazy to cache](https://github.com/vercel/next.js/blob/43410c992684670bc95dad25873f1ec89fed2f25/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.ts#L9). But the fetch response function is [url based](https://github.com/vercel/next.js/blob/43410c992684670bc95dad25873f1ec89fed2f25/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts#L242), i.e. no matter how many slots in the parallel routes, the url is the same, so the fetch responses generated are all the same, but it is creating and traversing sub cache for the number of slots, so it seems like the slots in the parallel routes are recognized as individual pages, not as component elements used by
layout.tsx in the same directory. I don't know if this is a design error or if it is intended and I am using it wrong.I think it should only make new request when the cache is stale.
It doesn't make new request in first 30 sec but it is making new request after it. Look like it doesn't update the cache
It doesn't make new request in first 30 sec but it is making new request after it. Look like it doesn't update the cache
yeah there are issue about this
West African LionOP
Oh, thank you. I'll try to add a comment.
yeah, tell them it work as expected when the duration is 5min