I'm using Next.js 14 with the app router.
I have two server components:
/products
that shows multiple products/products/{id}
.On /products/{id}
I check whether the id
is available in my products database with a server action and then render the product.
If the product is not available, I wanted to redirect the user back to /products
and show a toast message that the product was not found.
Redirecting is simple enough:
import { redirect } from 'next/navigation'
async function getNoProduct() {
return null
}
export default async function RedirectFromPage() {
let res = await getNoProduct()
if (!res === null) {
redirect('/products')
}
return (
<div>
<p>Hello SO</p>
</div>
)
}
However, I'm stuck implementing the toast message (using sonner toast via shadcn).
Directly adding the toast like below doesn't work because /products/{id}
is a server component and toast relies on useEffect,
e.g.
import { redirect } from 'next/navigation'
import { useToast } from '@/components/ui/use-toast'
async function getNoProduct() {
return null
}
export default async function RedirectFromPage() {
const { toast } = useToast()
let res = await getNoProduct()
if (!res === null) {
// Show toast
toast({
title: 'Error',
description: 'Product not found',
})
redirect('/products')
}
return (
<div>
<p>Hello SO</p>
</div>
)
}
will result in an error Error: useState only works in Client Components. Add the "use client" directive at the top of the file to use it. Read more: https://nextjs.org/docs/messages/react-client-hook-in-server-component
Based on another SO question related to server actions (here) I thought I could use cookies to communicate the error to /products
and then render a client component that uses the toast but ran into issues because I can trigger these server actions only from a client component.
How should I approach this? Is toast just the wrong type of component here or is there a better way to do this?
My understanding is the toast packages are meant for client side. For server side, I think you can create a loading.tsx page and nextjs will show the loading page then the intended page. If it’s a client side component inside a server rendered page, you should be able to use React Suspense to wrap the client side component in your server rendered page