DEV Community

Cover image for Add toast messages in Laravel with Wiretoast
Eduardo Lázaro
Eduardo Lázaro

Posted on

Add toast messages in Laravel with Wiretoast

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';
Enter fullscreen mode Exit fullscreen mode

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'),
    },
},
Enter fullscreen mode Exit fullscreen mode

Then the component goes once into your layout, and on the Vite path it injects no tags of its own.

<x-wiretoast />
Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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,
]);
Enter fullscreen mode Exit fullscreen mode

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 });
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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" />
Enter fullscreen mode Exit fullscreen mode

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" />
Enter fullscreen mode Exit fullscreen mode

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)