Fire toast notifications in Laravel from PHP, Alpine and plain JavaScript with one notify call, plus positioning, auto-dismiss and grouping, and no CSS framework in your bundle
Here is a problem I hit on every project. A Livewire action finishes and I need to tell the user it worked, but the toast library I grabbed assumes Tailwind, or ships its own huge runtime, or only works from JavaScript when half my triggers actually live in PHP. Wiretoast is my answer to that, and this post is the fast path to using it.
The problem
You want to fire a toast from PHP, from Alpine, and from plain JavaScript with the same call, and you do not want to drag a CSS framework into your bundle to get it.
How to install
Start with Composer, then wire up the assets. I bundle with Vite, so I import the package CSS and JS into my entry files.
// resources/js/app.js
import '@wiretoast/js/wiretoast.js';
import '@wiretoast/css/wiretoast.css';
That @wiretoast alias is optional, and you set it up by pointing Vite at the vendor resources folder so the imports stay short.
// vite.config.js
resolve: {
alias: {
'@wiretoast': path.resolve(__dirname, 'vendor/edulazaro/wiretoast/resources'),
},
},
Then the component goes once into your layout, and on the Vite path it injects no tags of its own.
<x-wiretoast />
How to use it
The fastest possible win is a one-liner in a Livewire component right after something succeeds. The helper is a component macro named notify, registered for you when Livewire is present.
$this->notify('Profile updated', 'success');
Under the hood that dispatches a notify browser event, which is exactly what Alpine fires too. So the same toast from a purely front-end button looks like this.
<button @click="$dispatch('notify', { message: 'Copied', type: 'info' })">
Copy link
</button>
The five types you can pass are success, error, warning, info and neutral, and a message can be a plain string or an object with a title and a message when you want a heading.
Positioning and auto-dismiss
Every toast takes an options object as the third argument, and the keys I reach for most are position, timeout and progress.
$this->notify('Heads up', 'warning', [
'position' => 'bottom-center',
'timeout' => 3000,
'progress' => true,
]);
There are seven positions available: top-left, top-center, top-right, bottom-left, bottom-center, bottom-right and center. A timeout of 0 makes the toast persist until it is closed by hand, and progress set to true draws a countdown bar that pauses while the pointer is over the toast.
Collapsing repeated toasts
If an action can fire many times in a row, stacking ten identical toasts is just noise. Set group to true and same-type messages fold into one.
window.notify('Item added', 'success', { group: true });
There is also a legacy shorthand, where passing true as the third argument on the Livewire helper means the same thing.
$this->notify('Item added', 'success', true);
Switching themes and dark mode
The visual style is chosen with the theme prop, and eleven themes are bundled, running from minimal to synthwave. You set it on the component.
<x-wiretoast theme="soft" />
Dark mode follows the operating system by default through prefers-color-scheme, and you can force a mode with the mode prop when you want deterministic output.
<x-wiretoast mode="dark" />
Do not mix the two install paths
This is the one thing that will bite you, so it is worth saying plainly. When you bundle with Vite the component injects no tags by design, which is why you import the CSS and JS yourself. If you instead publish the assets with vendor:publish --tag=wiretoast-assets, that is the moment you add :assets="true" to the component so it emits the link and script for you. Doing both, importing through Vite and passing :assets="true", loads everything twice.
Links
Wiretoast is MIT licensed and open source.
📌 You can see it in action here.
👉 Packagist: https://packagist.org/packages/edulazaro/wiretoast
👉 GitHub: https://github.com/edulazaro/wiretoast
Top comments (0)