ui
ui copied to clipboard
Toast is not displayed in useEffect during intial render
Hi,
Okay, I'm not sure if this is a shadcn ui issue or not ...
I'm developing Next.js 14 application with shadcn ui. I have a server action like this,
"use server";
...
export const createMessage = async (currentState) => {
return {
message: "Success";
};
}
then, I have a client component
"use client";
...
const Chat = () => {
// formState used for notification after server action
const [formState, formAction] = useFormState(createMessage, {
message: "empty",
});
useEffect(() => {
// this logged '{ message: "empty" }' in the initial rendering
console.log("%s", JSON.stringify(formState));
// the toast is not display during the initial render but it will show "success" after the following form is submitted.
toast({ description: formState.message });
}, [toast, formState]);
return (
<form action={formAction}>
<Button type="submit">Create message</Button>
</form>
);
}
export default Chat ;
Why the toast is not shown when the page was first loaded (and the useEffect was triggered and the empty message is logged in the console)?
The console.log function works immediately upon component render because it's a synchronous operation.
However, the toast function likely depends on the DOM being fully ready or a ToastProvider component being mounted in your app. If these conditions aren't met when the toast function is called, the toast notification might not display.
import { Toaster } from "@/components/ui/toaster"
export default function RootLayout({ children }) {
return (
<html lang="en">
<head />
<body>
<main>{children}</main>
<Toaster /> //this component might not be ready when the toast function is called
</body>
</html>
)
}
This explanation is based on my experience, There may be other causes for this issue but this is also a case.
im having a similar issue where <Toaster/> doesn't display toasts, But if I copy the content of <Toaster/> and paste it in my app it works fine
Try extracting the toast function from useEffect and wrap it inside a function:
"use client";
import * as React from "react";
import { toast } from "sonner";
export default function Home() {
const showToast = () => toast("hello");
React.useEffect(() => {
showToast();
}, [showToast]);
}
This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please leave a comment. Thank you.
This is the answer you are looking for, based on the official sonner docs https://sonner.emilkowal.ski/toast#render-toast-on-page-load
Render toast on page load To render a toast on initial page load it is required that the function toast() is called inside of a setTimeout or requestAnimationFrame.
setTimeout(() => { toast('My toast on a page load'); });
For me what worked is to put <Toaster /> in my _app.jsx along with timer.
_app.js
export default function App({ Component, pageProps }) {
return (
<UserProvider>
<Component {...pageProps} />
<Toaster />
</UserProvider>
)
}
Dashboard.jsx
useEffect(() => {
const timeoutId = setTimeout(() => {
toast.error('My toast on a page load');
}, 1000);
return () => clearTimeout(timeoutId); // Cleanup the timeout on component unmount
}, []);
Solution 1: For me, simply rendering the <Toaster /> component before <main>{children}</main> worked.
import { Toaster } from "@/components/ui/toaster"
export default function RootLayout({ children }) {
return (
<html lang="en">
<head />
<body>
<Toaster /> // Render it before the rest of the application
<main>{children}</main>
</body>
</html>
)
}
Solution 2: From docs:
useEffect(() => {
const timer = setTimeout(() => {
toast.info('your message here')
}, 100);
return () => clearTimeout(timer);
}, []);