Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Sonner Laravel Package

amjadiqbal/laravel-sonner

Laravel Sonner brings elegant, Sonner-inspired toast notifications to Laravel applications. Flash success, error, and info messages from controllers or Blade views and render them with a clean, customizable API, keeping user feedback consistent and simple to set up.

View on GitHub
Deep Wiki
Context7

Getting Started

This is the first stable release of the package, and it is effectively a rewrite: it now ships Sonner 2.0.8's stylesheet plus a plain-JavaScript port of the Toaster, so you get the real Sonner behaviour (stacking, swipe-to-dismiss, hover-to-expand, timers) without React.

Requirements: PHP 8.3+, Laravel 12 or 13. Livewire 3/4 is optional but first-class.

  1. Install the package and publish the config:
composer require amjadiqbal/sonner
php artisan vendor:publish --tag=sonner-config   # check the README for the exact tag
  1. Put the component once in your persistent layout, just before </body>:
<x-sonner />
  1. Sanity check with php artisan about — the release adds a line there, which is the fastest way to confirm the package booted and which config is in play.

First use case — toast on a redirect from a controller:

public function store(StorePostRequest $request)
{
    $post = Post::create($request->validated());

    toast()->success('Post saved')->description($post->title);

    return redirect()->route('posts.index');
}

The toast is queued during the request, flashed to the session, and rendered on the next full page load by <x-sonner />.

Implementation Patterns

One fluent API, two entry points. The toast() helper and the Sonner facade expose the same chainable surface (success/info/warning/error/loading/message/description/duration/persistent/position/action/cancel/closeButton/richColors/invert/dismissible/className/id). Use the helper for brevity in controllers and the facade where you want an injectable, mockable dependency in services.

Set defaults once, override rarely. Every <Toaster /> prop has a default in config/sonner.php, and the <x-sonner /> component accepts all of them. Configure position/theme/richColors globally, then override per-call only where a specific toast needs to stand out:

<x-sonner position="bottom-right" theme="system" :rich-colors="true" />

Livewire: same-response delivery. Calling toast()->success() inside a Livewire action renders in the same response — no extra round trip, no client-side listener to wire up. On a redirect, the toast waits for the next page. The old UsesSonner trait is gone; just call the helper.

Dispatching from Alpine / Livewire JS. Use the sonner:toast browser event rather than reaching into internals:

$dispatch('sonner:toast', { type: 'success', message: 'Copied' })

// or from a Livewire component
$this->dispatch('sonner:toast', type: 'error', message: 'Upload failed');

Browser API for client-driven flows. The JS surface mirrors Sonner (toast(), toast.success(), toast.promise(), toast.dismiss(), toast.getToasts()). toast.promise() is the workhorse for anything async — uploads, imports, wizard steps — and replaces the removed PHP promise():

toast.promise(saveDocument(), {
    loading: 'Saving…',
    success: 'Saved',
    error:  'Could not save',
});

Progress-style updates with a stable id. Give a loading toast an id and re-use it for the terminal state so the message mutates in place instead of stacking:

toast()->loading('Exporting…')->id('export');
// later
toast()->success('Export ready')->id('export')->action('Download', '/exports/1');

Long-lived notifications. Open toasts survive wire:navigate, so a toast raised before a navigation stays visible — useful for background jobs, but keep <x-sonner /> in the layout only. Mounting it inside a partial that gets swapped will double-render the toaster.

Persistent toasts with actions. Combine persistent(), action(), and closeButton() for "undo" or "review" affordances; the timer-based dismissal is what you're opting out of.

Gotchas and Tips

Two breaking changes to sweep for. Delete use AmjadIqbal\Sonner\Livewire\UsesSonner; from any Livewire component, and move any PHP promise() calls to toast.promise() in JavaScript.

Toast text is no longer HTML. Messages are set with textContent and the queue is JSON-encoded with <, >, & and quotes escaped. Anything that previously relied on markup in a toast (links, <strong>, line breaks) will now render literally. If you need structure, keep the text plain and use action()/cancel() for interactivity.

CSP needs a real nonce. The inline style and script are stamped with Laravel's CSP nonce. If you emit your own Content-Security-Policy header outside Laravel's nonce mechanism, or cache pages with a frozen nonce, the toaster's script will be blocked and you'll see nothing at all. First debugging step for "no toasts appear": check the browser console for CSP violations before you touch the config.

Flashed toasts are now consumed on render. Previously a queued toast could survive forever if no page rendered it; now it shows once and leaves the session. The practical consequence: a toast queued for a response that doesn't render <x-sonner /> (a JSON endpoint, a file download, a mail-preview route) can surface later on an unrelated page. Be explicit about where toasts are queued.

Don't double-fire in Livewire. Because same-response delivery is automatic, calling both toast()->success() and a manual $this->dispatch('sonner:toast', …) for the same action gives you two toasts.

Timers pause on hover and in hidden tabs. Short durations are not a reliable cleanup mechanism — a user returning to a backgrounded tab will still see toasts they "should" have missed. Use dismiss() or toast.getToasts() if you need deterministic state.

Escape hatches can trap users. persistent() + dismissible(false) + no closeButton() leaves a toast that only a programmatic dismiss() can remove. Always leave one exit.

The Alt+T hotkey is global. It focuses/expands the toaster region. Check for conflicts with your existing shortcuts, and be aware it is a new keyboard target for accessibility testing.

Dark theme + rich colors changed. Error and warning toasts now use a neutral background instead of Sonner's very dark red and olive. Any custom CSS or visual-regression baselines targeting the old colours need updating — style via className() rather than patching the bundled styles.css, which is Sonner 2.0.8 verbatim and will be replaced on upgrade.

It's a port, not React. The browser API list stops at toast(), success, error, warning, info, loading, promise, dismiss, getToasts. Don't assume React-only affordances like toast.custom with JSX/component content exist — pass strings and use className() for presentation.

Testing. In feature tests, assert on the flashed session payload for PHP-side toasts; for browser behaviour, assert against toast.getToasts() in a Pest browser/Dusk test rather than scraping DOM text, since stacking and animation make the DOM timing-dependent.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource