ui
ui copied to clipboard
Sonner toast messages show up underneath dialog backdrop-blue
If you trigger a Sonner toast when a Dialog is open, the toast message appears underneath the dialog backdrop element.
While it still looks correct. This means you cannot click the any buttons like the 'close' button on a toast or select the message for copying until you first click to dismiss the dialog.
Is there a workaround to fix this? I tried increasing the z-index of the toast container but that didn't work.
Hi @MarkLyck,
I have the same issue what fix mine was setting Modal to false on the Dialog.
Hi @MarkLyck ,
I tried to reproduce your issue using this code:
"use client";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { toast } from "sonner";
export default function Home() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline" onClick={() => toast("I'm your sonner")}>
Edit Profile
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>
Make changes to your profile here. Click save when you're done.
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="name" className="text-right">
Name
</Label>
<Input
id="name"
defaultValue="Pedro Duarte"
className="col-span-3"
/>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="username" className="text-right">
Username
</Label>
<Input
id="username"
defaultValue="@peduarte"
className="col-span-3"
/>
</div>
</div>
<DialogFooter>
<Button type="submit">Save changes</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
It appeared above the backdrop of the Dialog component:
If this is not your case, please share your code to understand the issue.
Hi @MarkLyck,
The problem lies in the DialogOverlay display, it overlays any content that isn't from the dialog itself, so when we put modal={false} in DialogContent, which removes it, the problem is solved.
@MenaiAla, sonner is displayed normally, but it isn't possible to perform any of his actions, such as dismiss or close.
I used your same code to reproduce the problem, but I added this to the Sonner provider to visualize it better:
<Sonner closeButton .../>
I've found a solution to click on the toast, but I haven't tested it to see if it affects anything.
I just added the group-[.toaster]:pointer-events-auto class to the toast styling in the Sonner provider:
<Sonner
toastOptions={{
classNames: {
toast:
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg group-[.toaster]:pointer-events-auto",
},
}}
...
/>
@henrysaantos I tried adding that, it's a bit better now, I don't have to click twice to dismiss a toast message. But clicking on the toast still dismisses the modal.
The best user experience would be to be able to interact with the toast without the dialog dismissing.
Setting Modal to false seems to be the best workaround for now.
@MarkLyck I had the same problem, but I didn't like the way the dialog had the modal property disabled, I solved it by adding a preventDefault in the onInteractOutside event of the DialogContent, and it seems to work well.
@MarkLyck I had the same problem, but I didn't like the way the dialog had the modal property disabled, I solved it by adding a preventDefault in the onInteractOutside event of the DialogContent, and it seems to work well.
Thanks, I did like this and it seems to work like a charm 👍:
onInteractOutside={e => {
const { originalEvent } = e.detail;
if ( originalEvent.target instanceof Element && originalEvent.target.closest('.group.toast') ) {
e.preventDefault();
}
}}
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.
I am using intercepting routes with modals so modifying the oninteractoutside wasn't a good option for me.
i was able to get the sonner toasts to show up on top of dialogs and alert dialogs by creating a portal like this:
"use client";
import { useTheme } from "next-themes";
import { Toaster as Sonner } from "sonner";
import { createPortal } from 'react-dom';
type ToasterProps = React.ComponentProps<typeof Sonner>;
const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme();
const toasterContent = (
<Sonner
theme={theme as ToasterProps["theme"]}
className="toaster group"
toastOptions={{
classNames: {
toast:
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
description: "group-[.toast]:text-muted-foreground",
actionButton:
"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
cancelButton:
"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
},
}}
{...props}
/>
);
// Only render in the browser
if (typeof window === 'undefined') return null;
return createPortal(toasterContent, document.body);
};
export { Toaster };
- Used createPortal to render the toaster directly into the document.body.
- Added a check to only render on the client-side to avoid SSR issues.