Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sonner toast is not rendering toast on component mount

I want to show a toast when my client side component mounts.
I'm using sonner library to show toast, it's working fine when we use button to show toast.

I make an example app to show the problem. Here is the github link https://github.com/saifion33/Nextjs-toast-problem

I'm trying to trigger a toast when component mount on client side.

app/layout.tsx

<html lang="en">
  <body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
    {children}
    <ToasterComp />
  </body>
</html>

ToasterComp

"use client"

import { Toaster } from "sonner"

const ToasterComp = () => {
  return (
    <Toaster closeButton richColors duration={3000} />
  )
}

export default ToasterComp

app/page.tsx

import MyComponent from "@/components/MyComponent";

export default function Home() {
  return (
    <div className="text-center">
      <h1 className=" my-10 text-blue-500 text-3xl">Sonner render problem example app</h1>
      <p className="text-slate-800">
        Reload this page, if you don&apos;t see a toast then this is not working. if you click the button you will see a toast.
      </p>
      <MyComponent />
    </div>
  );
}

MyComponent

"use client"

import { useEffect } from "react"
import { toast } from "sonner"

const MyComponent = () => {
  useEffect(() => {
    toast.success("This is the toast that will be shown.")
  }, [])

  return (
    <div>
      <button
        className="bg-blue-500 text-stone-50 border-2 border-slate-900 py-2 px-4 rounded-lg mt-4"
        onClick={() => toast.info('You clicked the button.')}
      >
        Show toast
      </button>
    </div>
  )
}

export default MyComponent
like image 529
saifi33 Avatar asked Oct 25 '25 22:10

saifi33


1 Answers

According to documentation:

To render a toast on initial page load it is required that the function toast() is called inside of a setTimeout or requestAnimationFrame.

Update to dispatch the toast in a timeout:

useEffect(() => {
  const timerId = setTimeout(() => {
    toast.success("This is the toast that will be shown.");
  });

  return () => {
    clearTimeout(timerId);
  };
}, []);

In the case that you render your React application into a React.StrictMode component that will double-mount the components and run certain lifecycle methods and hook callbacks twice and dispatch two toasts (!!), you can use a React Ref to track that you've already dispatched the mounting toast.

Example:

const mountedRef = useRef();

useEffect(() => {
  let timerId;

  if (!mountedRef.current) {
    setTimeout(() => {
      toast.success("This is the toast that will be shown.");
    });
  }

  mountedRef.current = true;

  return () => {
    clearTimeout(timerId);
  };
}, []);

Note that it is currently generally considered a React anti-pattern to use React refs to "track" when a component mounts to conditionally apply logic, a useEffect hook cleanup function should be returned to unsubscribe/cancel/cleanup up any pending/asynchronous logic. I believe this is a valid edge case since this sonner library doesn't export/include any way to cancel the dispatched toast from the first "initial render", so you'll need to not dispatch the toast on the second "initial render".

like image 147
Drew Reese Avatar answered Oct 27 '25 14:10

Drew Reese