How can I make an Error Boundary that allows retrying (remounting) a Server Component after it throw
Unanswered
Standard Chinchilla posted this in #help-forum
Standard ChinchillaOP
I’ve implemented a standard React Error Boundary and even tried wrapping it with a client-side retry mechanism — changing a key, hiding/showing the component, using router.refresh(), etc. None of these actually cause the Server Component to remount or re-render its children after an error has occurred.
I know that Next.js provides the error.tsx file at the route level, but that’s not what I want here. My component needs to manage its own error handling, since it will be reused across multiple pages, not just a single route.
How can I properly recover or re-render a Server Component (not a Client Component) after it has thrown an error, without using a route-level error.tsx boundary?
Is there any pattern or API in Next.js that allows retrying or remounting a failed Server Component from a client-side boundary?
I know that Next.js provides the error.tsx file at the route level, but that’s not what I want here. My component needs to manage its own error handling, since it will be reused across multiple pages, not just a single route.
How can I properly recover or re-render a Server Component (not a Client Component) after it has thrown an error, without using a route-level error.tsx boundary?
Is there any pattern or API in Next.js that allows retrying or remounting a failed Server Component from a client-side boundary?
43 Replies
@Standard Chinchilla I’ve implemented a standard React Error Boundary and even tried wrapping it with a client-side retry mechanism — changing a key, hiding/showing the component, using router.refresh(), etc. None of these actually cause the Server Component to remount or re-render its children after an error has occurred.
I know that Next.js provides the error.tsx file at the route level, but that’s not what I want here. My component needs to manage its own error handling, since it will be reused across multiple pages, not just a single route.
How can I properly recover or re-render a Server Component (not a Client Component) after it has thrown an error, without using a route-level error.tsx boundary?
Is there any pattern or API in Next.js that allows retrying or remounting a failed Server Component from a client-side boundary?
have you tried just refreshing the page if it produces an error?
there isn't any API in Next or in React where it would allow retrying or remounting a failed Server Component from a client-side boundary.
server components are evaluated in the server and no amount of client-side code can recover that unless you also handle the error in the server.
Nextjs's error.tsx file themselves are also not component-specific and just a catch-all uncaught error page in the server-rendering phase which resolves to a client component page.
the way i usually handle specific component error is by retrying the whole page since at that point that error would be very unexpected. errors in UIs like server components or async apis needs to be wrapped in try catch and handled in a non-error way (like redirection or rendering a different component). this way i dont really find React Error Boundary really useful unless its from a component i dont make/fancy external components.
there isn't any API in Next or in React where it would allow retrying or remounting a failed Server Component from a client-side boundary.
server components are evaluated in the server and no amount of client-side code can recover that unless you also handle the error in the server.
Nextjs's error.tsx file themselves are also not component-specific and just a catch-all uncaught error page in the server-rendering phase which resolves to a client component page.
the way i usually handle specific component error is by retrying the whole page since at that point that error would be very unexpected. errors in UIs like server components or async apis needs to be wrapped in try catch and handled in a non-error way (like redirection or rendering a different component). this way i dont really find React Error Boundary really useful unless its from a component i dont make/fancy external components.
@alfonsüs ardani have you tried just refreshing the page if it produces an error?
there isn't any API in Next or in React where it would allow retrying or remounting a failed Server Component from a client-side boundary.
server components are evaluated in the server and no amount of client-side code can recover that unless you also handle the error in the server.
Nextjs's error.tsx file themselves are also not component-specific and just a catch-all uncaught error page in the server-rendering phase which resolves to a client component page.
the way i usually handle specific component error is by retrying the whole page since at that point that error would be very unexpected. errors in UIs like server components or async apis needs to be wrapped in try catch and handled in a non-error way (like redirection or rendering a different component). this way i dont really find React Error Boundary really useful unless its from a component i dont make/fancy external components.
Standard ChinchillaOP
Thanks a lot for taking the time to reply — really appreciate it.
In the end I decided to stop using Server Components for data fetching. The issues I was running into just weren’t worth the relatively small UX benefits. I switched back to TanStack Query, which is what I’ve always used, and honestly the experience is basically the same. Most users won’t notice any difference between the two approaches anyway.
With TanStack Query I also get much better control over errors, retries and recovery, so it’s a much smoother setup for me.
Thanks again!
In the end I decided to stop using Server Components for data fetching. The issues I was running into just weren’t worth the relatively small UX benefits. I switched back to TanStack Query, which is what I’ve always used, and honestly the experience is basically the same. Most users won’t notice any difference between the two approaches anyway.
With TanStack Query I also get much better control over errors, retries and recovery, so it’s a much smoother setup for me.
Thanks again!
awesome
@alfonsüs ardani Click to see attachment
Milkfish
What do you do inside of the LikesErrorUi when the refresh button is clicked?
Do you run router.refresh() or do you refresh in a different way?
Do you run router.refresh() or do you refresh in a different way?
@Milkfish What do you do inside of the LikesErrorUi when the refresh button is clicked?
Do you run router.refresh() or do you refresh in a different way?
I run router refresh since its a server component error not a client component error. If things are cached properly, then only the LikesUI will be updated
@alfonsüs ardani I run router refresh since its a server component error not a client component error. If things are cached properly, then only the LikesUI will be updated
Milkfish
Aye, thanks.
The issue I had with the router.refresh was handling loading states.
I wanted to show a loading skeleton of the component while it's re-fetching it's data.
I tried using the "changing suspense key" hack to make the component show it's fallback ui. But for some reason, it only worked when one component's data failed to fetch.
I ended up doing a lil hack with date time that lets me refetch the data using router.refresh, show a loading animation, and also know when the data is done re-fetching so it can stop and redisplay the error component (that's if another error happens while re-fetching 🤷🏼♂️).
The issue I had with the router.refresh was handling loading states.
I wanted to show a loading skeleton of the component while it's re-fetching it's data.
I tried using the "changing suspense key" hack to make the component show it's fallback ui. But for some reason, it only worked when one component's data failed to fetch.
I ended up doing a lil hack with date time that lets me refetch the data using router.refresh, show a loading animation, and also know when the data is done re-fetching so it can stop and redisplay the error component (that's if another error happens while re-fetching 🤷🏼♂️).
@Milkfish Aye, thanks.
The issue I had with the router.refresh was handling loading states.
I wanted to show a loading skeleton of the component while it's re-fetching it's data.
I tried using the "changing suspense key" hack to make the component show it's fallback ui. But for some reason, it only worked when one component's data failed to fetch.
I ended up doing a lil hack with date time that lets me refetch the data using router.refresh, show a loading animation, and also know when the data is done re-fetching so it can stop and redisplay the error component (that's if another error happens while re-fetching 🤷🏼♂️).
Standard ChinchillaOP
Honestly, it’s not worth the hours and the headaches. I hope you get it sorted out, and when you do, if you have some free time, try implementing the same feature using React Query and tell me what the difference is — what’s the actual benefit of doing data fetching with Server Components instead of React Query? I’d really love to know
@Milkfish Aye, thanks.
The issue I had with the router.refresh was handling loading states.
I wanted to show a loading skeleton of the component while it's re-fetching it's data.
I tried using the "changing suspense key" hack to make the component show it's fallback ui. But for some reason, it only worked when one component's data failed to fetch.
I ended up doing a lil hack with date time that lets me refetch the data using router.refresh, show a loading animation, and also know when the data is done re-fetching so it can stop and redisplay the error component (that's if another error happens while re-fetching 🤷🏼♂️).
Suspense aren't meant to handle the traiditional fetching errors but you are indeed able to use it. If my guesses are correct, not only you need to change the Suspense key, but you also need a different cached async function that you used via
It isn't the best solution for highly interactive application. You can but it aint the best.
If you manage to use useState for loading animation, might as well follow tsmailok and use react query since that is what its best designed for.
Using rsc requries a different way of thinking things and sometimes its not worth the headache
use.It isn't the best solution for highly interactive application. You can but it aint the best.
If you manage to use useState for loading animation, might as well follow tsmailok and use react query since that is what its best designed for.
Using rsc requries a different way of thinking things and sometimes its not worth the headache
fwiw, i tried experimenting with RSC errors:
Morelet’s Crocodile
The benefits of data fetching on a server component mean that the server can fetch the data and then send the information to the client. Whereas on a client component, the data is not fetched until after the JavaScript is sent to the client and initialized and then the fetching begins, which also creates an extra trip from the the client to the database
@Morelet’s Crocodile The benefits of data fetching on a server component mean that the server can fetch the data and then send the information to the client. Whereas on a client component, the data is not fetched until after the JavaScript is sent to the client and initialized and then the fetching begins, which also creates an extra trip from the the client to the database
Standard ChinchillaOP
You’re right that Server Components technically fetch data “earlier,” but that benefit is mostly theoretical. In real apps, users can’t perceive that difference — it’s not a meaningful UX win.
The problems are noticeable though:
• No component-level error recovery (you can’t remount a failed Server Component).
• No retries at the component granularity.
• Forced splitting of UI because error boundary must be a client component.
•. Slow dev experience when using short cache lifetimes (cacheLife({expire: 3} --> this block your navigation in dev mode until fetch finish).
So the “extra trip avoided” doesn’t change UX, but the downsides absolutely affect developers and maintainability. This is why serious data-fetching still ends up happening in Client Components or with a client-side cache layer (React Query, TanStack Query, etc.).
The problems are noticeable though:
• No component-level error recovery (you can’t remount a failed Server Component).
• No retries at the component granularity.
• Forced splitting of UI because error boundary must be a client component.
•. Slow dev experience when using short cache lifetimes (cacheLife({expire: 3} --> this block your navigation in dev mode until fetch finish).
So the “extra trip avoided” doesn’t change UX, but the downsides absolutely affect developers and maintainability. This is why serious data-fetching still ends up happening in Client Components or with a client-side cache layer (React Query, TanStack Query, etc.).
Morelet’s Crocodile
I notice the difference in loading time and getting more content to the screen faster through server components. I'm lucky that I have not had the issues with the error boundaries that you speak off. For me having an error.txt handles my situations. But fetching the data on the server is hundreds of milliseconds faster, the only downside that I see myself is on paginated data, it's loaded slower when hopping backwards and forwards. I find it can sometimes be better to handle the data fetching on the client. But to each there own if initial load time and content to screen is important. I myself can notice the difference in load time when fetching on the client versus server especially with cache components and use cache.
Morelet’s Crocodile
I use a variety of server and client components depending on what I'm trying to accomplish, however, I am dead set on starting the promise on the page and then passing it the a server component or client component, with await for server and use() for client allowing me to fetch the data in parallel on page load wrapping them in suspense to allow those parts of the page to be dynamic
Morelet’s Crocodile
A little bit I tired using use cache private and it seems to help a bit, checkout https://dayssincelastvscodefork.patmac.ca/ and click threw the repos next and back you'll see the delay because I'm using search params to fetch the data
Morelet’s Crocodile
Lol I switched the DNS to vercell last night, I muse have messed that uo
@Morelet’s Crocodile A little bit I tired using use cache private and it seems to help a bit, checkout https://dayssincelastvscodefork.patmac.ca/ and click threw the repos next and back you'll see the delay because I'm using search params to fetch the data
fwiw, using search params to fetch the data shouldn't cause the delay if its properly cached
Morelet’s Crocodile
I'm still experimenting with 'use cache' and use cache private but the data is dynamic so while using cache components it will cache private or dynamic routes. But you still need to have the initial load
if the data isn't personalized per user, u shouldn't need "use cache: private". just check the auth outside the "use cache"d data and you should be good
Morelet’s Crocodile
Hey I'm able to use that link so I think the DNS is updated
i vpn'd to USoA and still can't access it
Morelet’s Crocodile
You're right, I don't have to use private
I cached it like this and the nav is not instant but is quicker
@Morelet’s Crocodile I cached it like this and the nav is not instant but is quicker
it would be nice if you enabled the android touch marks in dev settings so i can see the time when you pressed and when the UI updated 

Morelet’s Crocodile
It'd be even better if my website just worked LOL
hey im still refreshing 

Morelet’s Crocodile
I tried it on both my desktop and my phone and I've used incognito and it seems to be working
Morelet’s Crocodile
Not as fast as clicking through a paginated cached client component but still fairly quick. But yes, I would say paginated queries are definitely a little slower as a server component
Morelet’s Crocodile
Link
interesting
Morelet’s Crocodile
I use server components when I can and I use client components when I need interactivity, if I was going to load some data that had extensive amounts of paginated data that the user would be frequently clicking through, I would probably make it a client component, but for the most part, if it doesn't have interactivity I usually just make it a server component
im saying that that is quite long for cached searchparam navigation
it can be quicker
Morelet’s Crocodile
Im thinking i can add the page number to the tag
Morelet’s Crocodile
@alfonsüs ardani
'use cache'
cacheTag(`recent-forks-page-${page}`)
cacheLife({ stale: 150, revalidate: 60, expire: 300 })
try { much faster on the back click nowi haven't had to use custom cacheLife yet
Morelet’s Crocodile
The expire was important to me as I want fresh data when someone visits after it's been a while
awesome!
