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:
Manual Setup
If you prefer manual control:
'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;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:
| Prop | Type | Default | Description |
|---|---|---|---|
position | "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right" | "top-center" | The position where toasts are displayed |
toastOptions | ToastOptions | See below | Default options applied to all toasts |
expand | boolean | false | Expand toasts on hover |
richColors | boolean | false | Use rich colors for toast variants |
closeButton | boolean | false | Show a close button on toasts |
visibleToasts | number | 3 | Maximum number of visible toasts |
gap | number | 10 | Gap 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): stringSuccess Toast:
toast.success(message: string, options?: ToastOptions): stringWarning Toast:
toast.warning(message: string, options?: ToastOptions): stringError Toast:
toast.error(message: string, options?: ToastOptions): stringToastOptions
| Prop | Type | Default | Description |
|---|---|---|---|
description | string | undefined | Supporting text displayed below the message |
action | { label: string; onClick: () => void } | undefined | Action button with label and click handler |
duration | number | 4000 | Duration in milliseconds before auto-dismiss |
icon | ReactNode | undefined | Custom icon (overrides variant icon) |
closeButton | boolean | false | Show 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-motionsystem setting - Color Contrast: All text meets WCAG AA standards
- Status Announcements: Screen readers announce toast content
Best Practices
- Keep messages concise: Toast messages should be brief and scannable
- Use appropriate variants: Choose success, warning, or error based on the message type
- Limit duration: 4 seconds is the default duration to avoid overwhelming users
- Provide actionable feedback: Use descriptions to provide context when needed
- Avoid too many toasts: Stack only when necessary; avoid notification fatigue
- Include actions for important operations: Use action buttons for undo or retry
- 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.