MC Sonner

An extensible, accessible toast notification system for temporary alerts.

Overview

The mc-sonner is a core feedback component of the MicroClub UI library. Built on top of the powerful sonner primitive, it provides sleek, stackable notifications with world-class performance and accessibility, styled natively via Tailwind CSS v4.

Unlike standard toast implementations, this component has been custom-architected to seamlessly group icons, titles, and descriptions into a strict typographic hierarchy that perfectly matches MicroClub's Figma specifications. It features four distinct methods for creating toasts: default, success, warning, and error — each with automatic icon rendering and semantic styling.

As a part of the MicroClub library, this component is designed for instant deployment into your codebase, giving you full ownership of its layout, states, and behavior.

Preview

'use client';import McSonner, { toast } from '../ui/mc-sonner';export default function McSonnerDemo() {  return (    <div>      <button        onClick={() =>          toast('Event has been created', {            description: 'Sunday, December 03, 2023 at 9:00 AM',            action: {              label: 'undo',              onClick: () => console.log('Undo du toast simple cliqué'),            },          })        }        className="px-5 py-2.5 bg-primary text-background rounded-lg text-sm font-medium transition-all shadow-sm cursor-pointer"      >        Show Toast      </button>      <McSonner />    </div>  );}

Add to Your Project

Deploy the mc-sonner directly to your components/ui directory:

npx mcoli-ui@latest add mc-sonner
pnpm dlx mcoli-ui@latest add mc-sonner
yarn dlx mcoli-ui@latest add mc-sonner
bunx mcoli-ui@latest add mc-sonner

Manual Setup

If you prefer manual control:

Install the core dependencies
npm install sonner lucide-react
pnpm add sonner lucide-react
yarn add sonner lucide-react
bun add sonner lucide-react
Copy the source code
'use client';import { Toaster as SonnerComponent, toast as rawToast } from 'sonner';import { CheckCircle2, AlertCircle } from 'lucide-react';type ToasterProps = React.ComponentProps<typeof SonnerComponent>;const toastIcons = {  success: <CheckCircle2 className="text-muted-foreground shrink-0 h-3.5 w-3.5" />,  warning: <AlertCircle className="text-muted-foreground shrink-0 h-3.5 w-3.5" />,  error: <AlertCircle className="text-muted-foreground  shrink-0 h-3.5 w-3.5" />,};interface ToastOptions {  description?: string;  action?: {    label: string;    onClick: () => void;  };}const mcToastCustom = (  message: string,  variant?: 'success' | 'warning' | 'error',  options?: ToastOptions) => {  const Icon = variant ? toastIcons[variant] : null;  return rawToast(    <div className="w-full h-full flex flex-col items-start justify-center gap-0.5">      <div className="flex items-center gap-1.5 w-full max-w-66.5">        {Icon}        <span className="text-sm font-medium tracking-normal leading-5 text-card-foreground">          {message}        </span>      </div>      {options?.description && (        <p className="text-sm font-normal tracking-normal leading-5 text-muted-foreground max-w-66.5">          {options.description}        </p>      )}    </div>,    {      action: options?.action,    }  );};export const toast = Object.assign(  (message: string, options?: ToastOptions) => mcToastCustom(message, undefined, options),  {    success: (message: string, options?: ToastOptions) =>      mcToastCustom(message, 'success', options),    warning: (message: string, options?: ToastOptions) =>      mcToastCustom(message, 'warning', options),    error: (message: string, options?: ToastOptions) => mcToastCustom(message, 'error', options),  });const McSonner = ({ ...props }: ToasterProps) => {  return (    <SonnerComponent      className=""      position="top-center"      toastOptions={{        unstyled: true,        classNames: {          toast:            'w-97.5 min-h-18.5 bg-background flex items-center justify-between gap-1.5 p-4 border-[1px] rounded-lg shadow-xs border-border',          actionButton:            'flex items-center justify-center bg-primary text-background text-sm py-2 px-3.5 min-w-15.5 max-w-21.5 min-h-9 rounded-[8px] shadow-xs shrink-0 cursor-pointer',        },      }}      {...props}    />  );};export default McSonner;
Add the Toaster to your root layout

Place the McSonner component at the root of your application (typically in your main layout or app component) to ensure toasts can be displayed globally:

import McSonner from '@/components/ui/mc-sonner';

export default function RootLayout() {
  return (
    <html>
      <body>
        {/* Your content */}
        <McSonner />
      </body>
    </html>
  );
}

Usage

import { toast } from '@/components/ui/mc-sonner';

export default function Example() {
  return <button onClick={() => toast('Event has been created')}>Show Toast</button>;
}

Examples

Simple Toast (Default)

A basic notification without any variant styling. Perfect for general information messages.

<button
  onClick={() =>
    toast('Event has been created', {
      description: 'Sunday, December 03, 2023 at 9:00 AM',
    })
  }
>
  Show Default Toast
</button>

Success Toast

Displays a success notification with a checkmark icon. Ideal for confirming successful operations.

<button
  onClick={() =>
    toast.success('Changes saved successfully', {
      description: 'All your modifications have been preserved.',
    })
  }
>
  Show Success Toast
</button>

Warning Toast

Displays a warning notification with an alert icon. Use when you need to alert users about potential issues.

<button
  onClick={() =>
    toast.warning('This action cannot be undone', {
      description: 'Please proceed with caution.',
    })
  }
>
  Show Warning Toast
</button>

Error Toast

Displays an error notification with an alert icon. Perfect for communicating failed operations.

<button
  onClick={() =>
    toast.error('Payment processing failed', {
      description: 'Please check your payment method and try again.',
    })
  }
>
  Show Error Toast
</button>

With Action Button

Add an interactive action button to your toast for undo operations or additional user interactions.

<button
  onClick={() =>
    toast('Event has been created', {
      description: 'Sunday, December 03, 2023 at 9:00 AM',
      action: {
        label: 'Undo',
        onClick: () => console.log('Undo clicked'),
      },
    })
  }
>
  Show Toast with Action
</button>

Success with Action

Combine success feedback with an action button for enhanced user experience.

<button
  onClick={() =>
    toast.success('File uploaded successfully', {
      description: 'Your document is ready to share.',
      action: {
        label: 'View',
        onClick: () => console.log('View file'),
      },
    })
  }
>
  Show Success with Action
</button>

Error with Action

Provide users with an option to retry or get help when an error occurs.

<button
  onClick={() =>
    toast.error('Connection timeout', {
      description: 'Unable to reach the server. Please try again.',
      action: {
        label: 'Retry',
        onClick: () => console.log('Retry clicked'),
      },
    })
  }
>
  Show Error with Action
</button>

Long Description

Toast notifications automatically handle longer descriptions with proper text wrapping and readability.

<button
  onClick={() =>
    toast.success('Your account has been updated', {
      description:
        'All your profile information has been successfully updated including your name, email, and preferences. These changes are effective immediately.',
    })
  }
>
  Show Toast with Long Description
</button>

Multiple Toasts

Stack multiple toasts to display several notifications at once. They automatically arrange themselves in the configured position.

<button
  onClick={() => {
    toast('First notification');
    toast.success('Second notification');
    toast.warning('Third notification');
  }}
>
  Show Multiple Toasts
</button>

Programmatic Toast

Trigger toasts in response to events, API calls, or user actions from anywhere in your component.

'use client';
import { toast } from '@/components/ui/mc-sonner';
import { useState } from 'react';

export default function FormExample() {
  const [isLoading, setIsLoading] = useState(false);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setIsLoading(true);

    try {
      const response = await fetch('/api/submit', { method: 'POST' });
      if (response.ok) {
        toast.success('Form submitted successfully', {
          description: 'Thank you for your submission.',
        });
      } else {
        toast.error('Submission failed', {
          description: 'Please try again later.',
        });
      }
    } catch (error) {
      toast.error('An error occurred', {
        description: 'Please check your connection and try again.',
      });
    } finally {
      setIsLoading(false);
    }
  };

  return <form onSubmit={handleSubmit}>{/* form fields */}</form>;
}

Toast in Server Action

Use toasts to provide feedback from Next.js server actions on the client.

'use client';
import { toast } from '@/components/ui/mc-sonner';
import { updateUserProfile } from '@/app/actions';

export default function ProfileForm() {
  const handleUpdate = async (formData) => {
    try {
      const result = await updateUserProfile(formData);
      if (result.success) {
        toast.success('Profile updated', {
          description: 'Your changes have been saved.',
        });
      } else {
        toast.error('Update failed', {
          description: result.error || 'Please try again.',
        });
      }
    } catch (error) {
      toast.error('Something went wrong', {
        description: 'An unexpected error occurred.',
      });
    }
  };

  return <form action={handleUpdate}>{/* form fields */}</form>;
}

API Reference

McSonner Component Props

The McSonner component accepts all props from the underlying Sonner Toaster:

PropTypeDefaultDescription
position"top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right""top-center"The position where toasts are displayed
toastOptionsToastOptionsSee belowDefault options applied to all toasts
expandbooleanfalseExpand toasts on hover
richColorsbooleanfalseUse rich colors for toast variants
closeButtonbooleanfalseShow a close button on toasts
visibleToastsnumber3Maximum number of visible toasts
gapnumber10Gap between toasts in pixels
theme"light" | "dark" | "system""system"Color scheme for toasts

toast() Function

The toast() function and its variants return a toast ID for programmatic control.

Default Toast:

toast(message: string, options?: ToastOptions): string

Success Toast:

toast.success(message: string, options?: ToastOptions): string

Warning Toast:

toast.warning(message: string, options?: ToastOptions): string

Error Toast:

toast.error(message: string, options?: ToastOptions): string

ToastOptions

PropTypeDefaultDescription
descriptionstringundefinedSupporting text displayed below the message
action{ label: string; onClick: () => void }undefinedAction button with label and click handler
durationnumber4000Duration in milliseconds before auto-dismiss
iconReactNodeundefinedCustom icon (overrides variant icon)
closeButtonbooleanfalseShow a close button on this specific toast

Styling

The mc-sonner is pre-configured with MicroClub's design system. The default styling includes:

  • Toast Container: Bordered card with rounded corners and subtle shadow
  • Icons: Automatically rendered based on variant (success, warning, error)
  • Typography: Title in medium weight (500) with proper line height and color
  • Description: Secondary text with muted color for supporting information
  • Action Button: Styled with primary color, full height, and hover effects

All styling is applied via Tailwind CSS v4, making it fully customizable to your needs.

Accessibility

The mc-sonner is built on the accessible Sonner primitive and includes:

  • ARIA Roles: Proper semantic markup for screen readers
  • Focus Management: Keyboard navigation support for action buttons
  • Motion Preferences: Respects prefers-reduced-motion system setting
  • Color Contrast: All text meets WCAG AA standards
  • Status Announcements: Screen readers announce toast content

Best Practices

  1. Keep messages concise: Toast messages should be brief and scannable
  2. Use appropriate variants: Choose success, warning, or error based on the message type
  3. Limit duration: 4 seconds is the default duration to avoid overwhelming users
  4. Provide actionable feedback: Use descriptions to provide context when needed
  5. Avoid too many toasts: Stack only when necessary; avoid notification fatigue
  6. Include actions for important operations: Use action buttons for undo or retry
  7. Auto-dismiss non-critical messages: Critical errors might warrant longer durations

Customization

To customize the toast appearance globally, modify the toastOptions in the McSonner component:

const customOptions = {
  unstyled: false,
  classNames: {
    toast: 'custom-toast-class',
    actionButton: 'custom-action-button-class',
  },
};

For component-level customization, extend the existing Tailwind classes in your project's CSS.

On this page