Handling Data Refresh with Route Cache in a Server Component within Next.js
Answered
Miniature American Shepherd posted this in #help-forum
Miniature American ShepherdOP
Hello,
I am working on a task management application that categorizes tasks based on their statuses. The main page (index.tsx) fetches the tasks and has two child components for filtering and listing tasks respectively. Below the sample the code: https://gist.github.com/gaetansenn/6d624e8ced0f11afbe54f66d7bfb6c41
I have two queries regarding this implementation:
Is utilizing useState in list.tsx to manage the local tasks array the optimal approach?
When a task is deleted externally (e.g., via Postman), navigating to another page and returning to the listing page does not reflect the deletion until 30seconds as explained in the documentation. According to the Next.js caching documentation, options for cache invalidation include using revalidatePath, tags, or deleting cookies. However, it appears these options necessitate API hooks to trigger cache revalidation, which might not always be available. Could employing a client component resolve this issue? Your insights would be greatly appreciated.
Thank you!
I am working on a task management application that categorizes tasks based on their statuses. The main page (index.tsx) fetches the tasks and has two child components for filtering and listing tasks respectively. Below the sample the code: https://gist.github.com/gaetansenn/6d624e8ced0f11afbe54f66d7bfb6c41
I have two queries regarding this implementation:
Is utilizing useState in list.tsx to manage the local tasks array the optimal approach?
When a task is deleted externally (e.g., via Postman), navigating to another page and returning to the listing page does not reflect the deletion until 30seconds as explained in the documentation. According to the Next.js caching documentation, options for cache invalidation include using revalidatePath, tags, or deleting cookies. However, it appears these options necessitate API hooks to trigger cache revalidation, which might not always be available. Could employing a client component resolve this issue? Your insights would be greatly appreciated.
Thank you!
Answered by fuma
Is utilizing useState in list.tsx to manage the local tasks array the optimal approach?Yes, that's what most of the applications did.
When a task is deleted externallyIs it happening regularly? For real-time updating the task list, Next.js doesn't provide a built-in way to push real-time notifications to client, you may look for a real-time messaging provider, such as Pusher Channels.
In general, client side data-fetching libraries may be better (e.g. SWR, React Query). You can pre-fetch data by passing them from a Server Component, and they also support advanced revalidation that Next.js doesn't provide. For example, revalidate data after re-focusing the window
65 Replies
Is utilizing useState in list.tsx to manage the local tasks array the optimal approach?Yes, that's what most of the applications did.
When a task is deleted externallyIs it happening regularly? For real-time updating the task list, Next.js doesn't provide a built-in way to push real-time notifications to client, you may look for a real-time messaging provider, such as Pusher Channels.
In general, client side data-fetching libraries may be better (e.g. SWR, React Query). You can pre-fetch data by passing them from a Server Component, and they also support advanced revalidation that Next.js doesn't provide. For example, revalidate data after re-focusing the window
Answer
@fuma > Is utilizing useState in list.tsx to manage the local tasks array the optimal approach?
Yes, that's what most of the applications did.
> When a task is deleted externally
Is it happening regularly? For real-time updating the task list, Next.js doesn't provide a built-in way to push real-time notifications to client, you may look for a real-time messaging provider, such as Pusher Channels.
In general, client side data-fetching libraries may be better (e.g. SWR, React Query). You can pre-fetch data by passing them from a Server Component, and they also support advanced revalidation that Next.js doesn't provide. For example, revalidate data after re-focusing the window
Miniature American ShepherdOP
Hello @fuma thank you so much for your reply 🙂
About when task is deleted externally it was just a case / exemple to more understand the new version of next using app router and find a way to always having the page.tsx updated when the user is going to page using Link component. Because i can see that it's not possible to disable the Router cache for specific page.
So your advise is that using client side data-fetching is a better way ? I asume server component is more opitmized in term of payload no ? I checked the SWR library but only saw using getStaticProps injection props fallback but i asume this is not a function to use with app router with next 13 no ?
About when task is deleted externally it was just a case / exemple to more understand the new version of next using app router and find a way to always having the page.tsx updated when the user is going to page using Link component. Because i can see that it's not possible to disable the Router cache for specific page.
So your advise is that using client side data-fetching is a better way ? I asume server component is more opitmized in term of payload no ? I checked the SWR library but only saw using getStaticProps injection props fallback but i asume this is not a function to use with app router with next 13 no ?
You can change it to App Router manually, by passing the default data as a prop
@fuma You can change it to App Router manually, by passing the default data as a prop
Miniature American ShepherdOP
Do you have an example ? 🙂
No example needed
'use client'
function ClientConfig({ data }) {
return <SWRConfig value={{ data }}>...</SWRConfig>
}That's a classic use case of Server Component
Pass props to client component
I asume server component is more opitmized in term of payload noNot when you need advanced revalidation functionality
You can utilize server components for pre-fetching data though
Miniature American ShepherdOP
Sorry i'm just starting with Next / React. So you mean that my index.tsx is still a server component and i pass the data fetched to a client component ?
Yes
Miniature American ShepherdOP
I did this but the data is still not updated because i asume the Route cache is global no ?
import Client from '@/app/components/client'
async function getData(status: string) {
return (await fetch(`${process.env.NEXT_PUBLIC_APP_BASE_URL}/api${status ? `?status=${status}` : ''}`, {
// Used to invalidate api call
cache: 'no-store'
})).json()
}
export default async function Home({ searchParams: { status } }: { searchParams: { status: string } }) {
const tasks = await getData(status)
return (
<>
<Client data={tasks} />
</>
)
}'use client'
import Filter from '@/app/components/filter'
import List from '@/app/components/list'
import { SWRConfig } from 'swr'
export default function Client({ data }: { data: any }) {
return (
<SWRConfig value={{ data }}>
<div className="app-container container-y">
<Filter />
<List tasks={data} className="mt-5" />
</div >
</SWRConfig>
)
}You have to fetch data via SWR in order to get revalidate functionality,
SWRConfig only works for SWR@fuma You have to fetch data via SWR in order to get revalidate functionality, `SWRConfig` only works for SWR
Miniature American ShepherdOP
Have you got this type of error when importing useSWR ?
Attempted import error: 'swr' does not contain a default export (imported as 'useSWR').here my code:
import Client from '@/app/components/client'
import useSWR from 'swr'
const fetcher = (url) => fetch(url).then((res) => res.json())
export default function Home({ searchParams: { status } }: { searchParams: { status: string } }) {
const { data } = useSWR(`${process.env.NEXT_PUBLIC_APP_BASE_URL}/api${status ? `?status=${status}` : ''}`, fetcher)
return (
<>
<Client data={data} />
</>
)
}Surely you hadn't install the library correctly
Miniature American ShepherdOP
it's not just yarn add swr ?
maybe it's because i used bun but dont think so
I'm using pnpm, so I can't help with dependency issues with yarn
@fuma I'm using pnpm, so I can't help with dependency issues with yarn
Miniature American ShepherdOP
it's not yarn but bun. I'm going to try with pnpm 🙂
If d.ts files are not missing, It's likely a package manager or tsconfig problem
Miniature American ShepherdOP
Hum look's like this is not an issue with dependecy
the next project is the default configuration i have created yesterday with npx create-next-app@latest
You can try pnpm if bun doesn't work great
Miniature American ShepherdOP
ok it's normal :
In Next.js App Router, all components are React Server Components (RSC) by default. You could only import the key serialization APIs from SWR in RSC.import { unstable_serialize } from 'swr' // ✅ works
import { unstable_serialize as infinite_unstable_serialize } from 'swr/infinite' // ✅ works
import useSWR from 'swr' // ⌠it won't worksMiniature American ShepherdOP
Maybe I did wrong i tought that i need to replace fetch with useSWR in the server component
😅
useSWR is a hook, it re-renders the component when state is changed/revalidated. Hence it must be used in a client componentMiniature American ShepherdOP
Okay i see i have tried this but look's like data from the Repo component is undefined:
and the page.tsx is this
'use client'
import useSWR from 'swr'
import Filter from '@/app/components/filter'
import List from '@/app/components/list'
import { SWRConfig } from 'swr'
function Repo({ status }) {
const { data, error } = useSWR(`${process.env.NEXT_PUBLIC_APP_BASE_URL}/api${status ? `?status=${status}` : ''}`);
console.log('data is from repo', data)
return (
<div className="app-container container-y">
<Filter />
<List tasks={data} className="mt-5" />
</div >
)
}
export default function Client({ data, status }: { data: any, status: any }) {
return (
<SWRConfig value={{ data }}>
<Repo status={status}></Repo>
</SWRConfig>
)
}tsxand the page.tsx is this
import Filter from '@/app/components/filter'
import List from '@/app/components/list'
import Client from '@/app/components/client'
async function getData(status: string) {
return (await fetch(`${process.env.NEXT_PUBLIC_APP_BASE_URL}/api${status ? `?status=${status}` : ''}`, {
// Used to invalidate api call
cache: 'no-store'
})).json()
}
export default async function Home({ searchParams: { status } }: { searchParams: { status: string } }) {
const tasks = await getData(status)
return (
<>
<Client data={tasks} status={status} />
</>
)
}Miniature American ShepherdOP
I have added the fetcher method but doesnt work 😦
There is no offical exemple documentation using app router with server component ?
There is no offical exemple documentation using app router with server component ?
const fetcher = (...args) => fetch(...args).then(res => res.json())
function Repo({ status }) {
const { data, error } = useSWR(`${process.env.NEXT_PUBLIC_APP_BASE_URL}/api${status ? `?status=${status}` : ''}`, fetcher);
console.log('data is from repo', data)
return (
<div className="app-container container-y">
<Filter />
<List tasks={data} className="mt-5" />
</div >
)
}Make sure your fetcher function is working properly
Miniature American ShepherdOP
From this example it's not pointing how to provide the fallback / data to the swr-provider client. fetcher function is working properly ? it's not just a fetch wrapper ?
Not going to work if the request failed on the client side. And fallback is just a prop, you can easily figure it out with the intellisense of your code editor
Miniature American ShepherdOP
Still dont understand why there is no examples in the doc or in the repository using app router ...
@fuma https://swr.vercel.app/docs/with-nextjs#client-components
That’s just the example
You have to spend some time investigating how to use SWR I guess. Also log values with
console.log and utilise devtools of your browser to fix underlying problemsSurely it works for me perfectly
Miniature American ShepherdOP
Quite lost in your example we have app/page.tsx with client component so not my case and the other example just pointing how to create a swr-provider that could be use in server component but still dont understand how to pass the initial data from server component fetching
You really have to read this:
https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns
https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns
Miniature American ShepherdOP
Yes but with SWR we fetch the data in client component so it's not 100% true that we dont fetch data in client component.
From my case if I want to handle the ssr feature with client component i have to pass the data from the server component to the client component as it's explained when using SWR with fallback props https://swr.vercel.app/docs/with-nextjs#pre-rendering-with-default-data but this is not an example using app router. I have tried your example injecting the data from server component to client component from prop with this but I also cant find an exemple using this
From my case if I want to handle the ssr feature with client component i have to pass the data from the server component to the client component as it's explained when using SWR with fallback props https://swr.vercel.app/docs/with-nextjs#pre-rendering-with-default-data but this is not an example using app router. I have tried your example injecting the data from server component to client component from prop with this but I also cant find an exemple using this
<SWRConfig value={{ data }}>it's not 100% true that we dont fetch data in client component.Don't know where you hear it, but "we dont fetch data in client component" is totally wrong and can be misleading.
but this is not an example using app routerThat's what above have suggested you to do:
1. mark components that uses
useSWR as client component2. Create
swr-provider3. Wrap your page content with the SWR provider. Your page should remains a server component
You can easily pass the fallback to
SWRConfig from the providerMiniature American ShepherdOP
Miniature American ShepherdOP
This is how i have injected the data from the server component to client component. From this example look's like the data from ssr side is undefined
import Filter from '@/app/components/filter'
import List from '@/app/components/list'
import Client from '@/app/components/client'
async function getData(status: string) {
return (await fetch(`${process.env.NEXT_PUBLIC_APP_BASE_URL}/api${status ? `?status=${status}` : ''}`, {
// Used to invalidate api call
cache: 'no-store'
})).json()
}
export default async function Home({ searchParams: { status } }: { searchParams: { status: string } }) {
const tasks = await getData(status)
return (
<>
<Client fallback={tasks} status={status} />
</>
)
}'use client'
import useSWR from 'swr'
import Filter from '@/app/components/filter'
import List from '@/app/components/list'
import { SWRConfig } from 'swr'
const fetcher = async (...args) => {
console.log('fetcher called')
return fetch(...args).then(res => res.json())
}
function Repo({ status }) {
const { data, error } = useSWR(`${process.env.NEXT_PUBLIC_APP_BASE_URL} / api${status ? `?status=${status}` : ''}`, fetcher);
console.log('data is from repo', data, error)
return (
<div className="app-container container-y">
<Filter />
<List tasks={data || []} className="mt-5" />
</div >
)
}
export default function Client({ fallback, status }: { fallback: any, status: any }) {
return (
<SWRConfig value={{ fallback }}>
<Repo status={status}></Repo>
</SWRConfig>
)
}i assume i did a mistake with the SWRConfig data injection by default
Miniature American ShepherdOP
Okay i found the problem
@Miniature American Shepherd Okay i found the problem
may i ask what the problem was?
Miniature American ShepherdOP
I was not injecting the good fallback path using uniq key:
import Filter from '@/app/components/filter'
import List from '@/app/components/list'
import Client from '@/app/components/client'
import { getTaskUrl } from './components/fetcher'
async function getData(status: string) {
return (await fetch(getTaskUrl(status), {
// Used to invalidate api call
cache: 'no-store'
})).json()
}
export default async function Home({ searchParams: { status } }: { searchParams: { status: string } }) {
const tasks = await getData(status)
const fallback = {
[`${getTaskUrl(status)}`]: tasks
}
return (
<Client fallback={fallback} status={status} />
)
}And from the client tsx page
'use client'
import useSWR from 'swr'
import Filter from '@/app/components/filter'
import List from '@/app/components/list'
import { SWRConfig } from 'swr'
import { getTaskUrl, fetcher } from './fetcher'
function Repo({ status }: { status: string }) {
const { data, error } = useSWR(getTaskUrl(status), fetcher);
return (
<div className="app-container container-y">
<Filter />
<List tasks={data} className="mt-5" />
</div >
)
}
export default function Client({ fallback, status }: { fallback: any, status: any }) {
return (
<SWRConfig value={{ fallback }}>
<Repo status={status}></Repo>
</SWRConfig>
)
}Miniature American ShepherdOP
But from what I can see is that using swr is injecting payload in the rended html page and used in the client side in the client component.
Compare to server component data injected to children as prop is a different technical approch.
So i assume using swr should be only used if you need to bypass the Route cache problem.
Compare to server component data injected to children as prop is a different technical approch.
So i assume using swr should be only used if you need to bypass the Route cache problem.
@riský may i ask what the problem was?
Miniature American ShepherdOP
You was also facing this kind of problem using app router ?
nah, i was just wondering what occured to help others if they see it (and was kinda intrested)
thank you for your indepth response, most either don't or do very basic 🙂
Miniature American ShepherdOP
I was attempting to find some answers as the concepts seem quite vague. The Next.js documentation advises primarily using server components for data fetching. However, the Route cache presents some limitations concerning cache invalidation in client components during navigation. Moreover, the revalidation of the cache should be customizable for a specific page or route key.
@Miniature American Shepherd But from what I can see is that using swr is injecting payload in the rended html page and used in the client side in the client component.
Compare to server component data injected to children as prop is a different technical approch.
So i assume using swr should be only used if you need to bypass the Route cache problem.
Not to "bypress" but for advanced revalidation functionality. Next.js Caching API isn't a magic, overusing it will cause performance issues on your server.
When you wanted to re-fetch data on the client side, it is worth nothing to achieve it using hacky ways with Next.js Caching API.
In this use case, a client side data-fetching solution might be what you're looking for. You can use both of them in the same application.
I can't explain all the concepts of Next.js since it's hard enough to explain the whole caching feature of Next.js, and I don't have that much of time either
When you wanted to re-fetch data on the client side, it is worth nothing to achieve it using hacky ways with Next.js Caching API.
In this use case, a client side data-fetching solution might be what you're looking for. You can use both of them in the same application.
I can't explain all the concepts of Next.js since it's hard enough to explain the whole caching feature of Next.js, and I don't have that much of time either

Next.js team is currently focusing on dev server performance issues, don't sure when we can customise expire time of Router Cache of a page
(btw we have https://discord.com/channels/752553802359505017/1163033868207673354 for discussing about SWR, you can try other solutions like React Query if you prefer)
Miniature American ShepherdOP
I agree with you on the point of using either the client component version or the server component version using SWR. I just think that for someone who is getting started with development, it can quickly become complicated to know what to use. It could have been good to have an example of client-side fetching with payload injection from the server component and explain why this type of context should be used in certain cases. I come from the Vue / Nuxt community and frankly in terms of simplicity, by wanting to put cache everywhere, we also lose the developer in terms of how to do things right and without making mistakes. Thank you anyway for your feedback and your help. 🙂