shadcn-ui / ui

Beautifully designed components that you can copy and paste into your apps. Accessible. Customizable. Open Source.
https://ui.shadcn.com
MIT License
74.02k stars 4.56k forks source link

variants of sonner #2254

Closed FleetAdmiralJakob closed 10 months ago

FleetAdmiralJakob commented 10 months ago

An error and an success variant of sonner would be a great addition

jrTilak commented 10 months ago

Hi @FleetAdmiralJakob,

Thanks for your suggestion. You can actually already use variants. Here's an example:

   toast.success('Event has been created')
   toast.error('Event has not been created')
  // warning, info etc 

Check here for more details : https://sonner.emilkowal.ski/

And another thing, you can also style the sooner with css as well as tailwind css also, like this with Toaster component

<Toaster
  toastOptions={{
    unstyled: true,
    classNames: {
      error: 'bg-red-400',
      success: 'text-green-400',
      warning: 'text-yellow-400',
      info: 'bg-blue-400',
    },
  }}
/>

or also with toast function:

toast('Hello World', {
  unstyled: true,
  classNames: {
    toast: 'bg-blue-400',
    title: 'text-red-400 text-2xl',
    description: 'text-red-400',
    actionButton: 'bg-zinc-400',
    cancelButton: 'bg-orange-400',
    closeButton: 'bg-lime-400',
  },
});

as mentioned here in the documentation

For more advanced usages, you can directly check out the documentation: https://sonner.emilkowal.ski/getting-started

Feel free to ask any questions if you are not satisfied with the answer.

javed24 commented 9 months ago

@jrTilak would really appreciate some pointers in terms of what I'm missing here -

import { toast } from 'sonner';

//...

return(
 <Button
        onClick={() => {
          toast.error('my dummy error message', {
            unstyled: true,
            duration: 9000000,
            classNames: {
              error: 'bg-red-400',
              info: 'bg-blue-400',
              success: 'bg-green-400',
              warning: 'bg-orange-400',
              toast: 'bg-blue-400',
              title: 'text-red-400 text-2xl',
              description: 'text-red-400',
              actionButton: 'bg-zinc-400',
              cancelButton: 'bg-orange-400',
              closeButton: 'bg-lime-400',
            },
          });
        }}
      >
        <div className="flex items-center justify-center">
          <span>Show Error Toast</span>
        </div>
      </Button>
)

I understand that a lot more styles will have to go there if I'm opting for unstyled. However, it doesn't look like any of those styles are taking into action, as this is all I'm getting -

Screenshot 2024-02-01 at 11 39 58 AM

What's interesting though is that looks like only the text-2xl portion from title: 'text-red-400 text-2xl', is taking into action, but nothing else. Any thoughts on what I'm missing here?

And in case it helps, my components/ui/sonner.tsx is exactly as it is after it gets installed and following is how I've added Toaster at the global level -

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <Toaster />
      <AuthProvider>
        <RouterProvider router={router} />
      </AuthProvider>
      <ReactQueryDevtools />
    </QueryClientProvider>
  </React.StrictMode>,
);
jrTilak commented 9 months ago

Yes, You are right.

I also noticed that the styles are not being applied using the method you are using.

After doing some trials here are my findings:

  1. The classes we applying are being applied to the element but the problem is that they does not work because default styles are overwriting them.
    • Example : When wen apply bg-red-400 , the default style background-color: hsl(var(--background)); overwrites the style.
  2. You mentioned that text-2xl is working , it is because there is no default font size. It maybe bug or something , I guess The unstyled=true is not working properly maybe.

https://github.com/shadcn-ui/ui/assets/106688422/988409fb-64a2-4e25-a597-edeaa30a26af

Solution

For now you can provide the same styles to <Toaster/> instead of toast() like this and it works as expected.

<Toaster  toastOptions={{
            unstyled: true,
            duration: 9000000,
            classNames: {
              error: 'bg-red-400',
              info: 'bg-blue-400',
              success: 'bg-green-400',
              warning: 'bg-orange-400',
              toast: 'bg-blue-400',
              title: 'text-red-400 text-2xl',
              description: 'text-red-400',
              actionButton: 'bg-zinc-400',
              cancelButton: 'bg-orange-400',
              closeButton: 'bg-lime-400',
            },
          }}/>

OR,

you can directly import the <Toaster/> from sooner

import { Toaster } from 'sonner';

BUG

I think it is some bug from shadcn as sooner is working fine when we import form sooner and does not work when import form shadcn.

I guess you should open a issue, if you want.

If you are not satisfied with the answer, you can ask more❤️

javed24 commented 9 months ago

thanks for looking into it @jrTilak , appreciate your insight!

in my case, using toaster from sonner is a better and more scalable option than the component (<Toaster/>) flavor of it. I am importing sonner directly from sonner in my component (as well as importing <Toaster/> directly from sonner in my main.tsx. I just happened to install it through shadcn. So I'm not quite sure if it's necessarily a bug from shadcn.

Having said that, I think you're right regarding there being some sort of style overriding going on. What I'm not sure about though is the source - this style overriding seems to be originating from the library itself (?), i.e., in the video, where you're inspecting the styles for the background color, it's coming from the .group.toaster class(es), which are coming from the library, right?

As a workaround, what I found working for me is the following -

toast.custom(
            (t) => {
              return (
                <div className="flex justify-between">
                  <div className="flex items-center">
                    <Alert />
                    <div className="ml-2">This is a dummy error message</div>
                  </div>
                  <Button
                    className="text-white"
                    variant="link"
                    onClick={() => toast.dismiss(t)}
                  >
                    <Cancel />
                  </Button>
                </div>
              );
            },
            {
              unstyled: true,
              duration: 9000000,
              description: 'some generic text',
              classNames: {
                toast:
                  'bg-green-100 rounded-lg py-2 px-4 shadow-lg text-white w-96',
              },
            },
          );

Basically putting all my styles in the toast class and using custom jsx.

jrTilak commented 9 months ago

Yeah, there is some style overriding , I think it is from shadcn because if you inspect the component <Toaster/> that you installed from shadcn, It has some styles overriding done using the tailwind variables to make the design consistent

<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}
    />

It is good things as it makes us our design consistent but it also made that drawback to overriding the styles.

As I mentioned earlier, if you pass the style props to the <Toaster/>, it works as expected because it knows you are passing the props and use the given styles but if you pass the style props from the function it has no way to know that you are passing the styles because the Toaster is from shadcn and toast function is from sooner .

That's it from me.

And glad to know you found the solution helpful.

javed24 commented 9 months ago

yeah, that makes sense. Guess the best path forward in this case would be to use Toaster from shadcn whenever applicable (and the passed styles thru classNames will take over), and use toast from sonner in other cases and make use of the approach I've laid out above. Thanks!

nelwincatalogo commented 7 months ago

I prefer variant like this

image

Just add something like this to the toastOptions

// components/ui/sonner.tsx

<Sonner
      theme={theme as ToasterProps['theme']}
      className="toaster group"
      toastOptions={{
        classNames: {
         ...
          icon: 'group-data-[type=error]:text-red-500 group-data-[type=success]:text-green-500 group-data-[type=warning]:text-amber-500 group-data-[type=info]:text-blue-500',
        },
      }}
      {...props}
    />
aryaadinulfadlan commented 7 months ago

I prefer variant like this

image

Just add something like this to the toastOptions

// components/ui/sonner.tsx

<Sonner
      theme={theme as ToasterProps['theme']}
      className="toaster group"
      toastOptions={{
        classNames: {
         ...
          icon: 'group-data-[type=error]:text-red-500 group-data-[type=success]:text-green-500 group-data-[type=warning]:text-amber-500 group-data-[type=info]:text-blue-500',
        },
      }}
      {...props}
    />

hi sir, Where can I get all the props or properties related to this sonner? because I need more customization

nelwincatalogo commented 7 months ago

I prefer variant like this image Just add something like this to the toastOptions

// components/ui/sonner.tsx

<Sonner
      theme={theme as ToasterProps['theme']}
      className="toaster group"
      toastOptions={{
        classNames: {
         ...
          icon: 'group-data-[type=error]:text-red-500 group-data-[type=success]:text-green-500 group-data-[type=warning]:text-amber-500 group-data-[type=info]:text-blue-500',
        },
      }}
      {...props}
    />

hi sir, Where can I get all the props or properties related to this sonner? because I need more customization

You can follow the installation guide here: https://ui.shadcn.com/docs/components/sonner

After the installation, just paste the following code in components/ui/sonner.tsx toastOptions

icon: 'group-data-[type=error]:text-red-500 group-data-[type=success]:text-green-500 group-data-[type=warning]:text-amber-500 group-data-[type=info]:text-blue-500',
jekigates commented 4 months ago
// sonner.tsx
import { AlertTriangle, CheckCircle, Info, Loader, XCircle } from "lucide-react"
import { useTheme } from "next-themes"
import { Toaster as Sonner } from "sonner"

type ToasterProps = React.ComponentProps<typeof Sonner>

const Toaster = ({ ...props }: ToasterProps) => {
    const { theme = "system" } = useTheme()

    return (
        <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",
                },
            }}
            icons={{
                success: <CheckCircle className="h-4 w-4 text-green-500" />,
                info: <Info className="h-4 w-4 text-blue-500" />,
                warning: <AlertTriangle className="h-4 w-4 text-amber-500" />,
                error: <XCircle className="h-4 w-4 text-red-500" />,
                loading: <Loader className="h-4 w-4 text-gray-500 animate-spin" />,
            }}
            {...props}
        />
    )
}

export { Toaster }

Example how to use it :

toast.success("Success", {
    description: "Profile updated successfully",
})
toast.error("Custom Title 1", {
    description: "Failed to update profile",
 }) 
Cathykatinti commented 3 months ago

return ( <Sonner theme={theme as ToasterProps['theme']} className="toaster group" toastOptions={{ classNames: { toast: 'group toast group-[.toaster]:bg-background 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', error: 'bg-red-100 text-red-800 border border-red-400', success: 'bg-green-100 text-green-800 border border-green-400', warning: 'bg-yellow-100 text-yellow-800 border border-yellow-400', info: 'bg-blue-100 text-blue-800 border border-blue-400', just get rid off the default text color and everything will be oukay ,

Cathykatinti commented 3 months ago

text-foreground group-[.toaster] get rid of this line and all will be a success