Tilly The Coder
Tilly The Coder
NativePHP (desktop) + Livewire 3 in 5 Minutes: Click a Button, Fire a Native Notification
Tilly The Coder

NativePHP (desktop) + Livewire 3 in 5 Minutes: Click a Button, Fire a Native Notification

Tilly The Coder
6 mins
This is the estimated time it takes to read the article.

Want the quickest possible path from “fresh Laravel app” to “my app shows a real OS notification”? Here’s a tiny, production-grade starting point that pairs NativePHP (Electron runtime) with Livewire 3. We’ll install the bits, add a single Livewire component, and wire up a button that triggers a native desktop notification—no custom JS required.

Along the way we’ll note a few Livewire-3 specifics (like the unified dispatch() API replacing dispatchBrowserEvent() in v2) and mention the modern Livewire starter kit that ships with Flux UI.

What we’ll build

  • A Livewire component at /native-notify with one button: “Show Native Notification”

  • When clicked (inside the NativePHP shell), it uses NativePHP’s Notification facade to display a real OS notification.

Heads-up: Native notifications appear when you run the app in the native shell (php artisan native:serve). In a plain browser tab you won’t see the OS notification.

Photo of the application we will build. Contains title and message and a button to trigger notification. Notification shown in corner

Photo of the application we will build.

Requirements

  • PHP 8.3+, Laravel 11/12+, Node 22+ (NativePHP recommends modern local PHP & Node; Laravel 12 works great).

  • An existing Laravel app or the official Laravel Starter Kit (Livewire flavour) which includes Flux UI out of the box (optional).

1) Install Livewire 3 (if not using the starter kit)

composer require livewire/livewire

Livewire auto-injects its assets by default when a component is on the page. If you prefer manual control, you can use @livewireStyles/@livewireScripts, or switch to ESM with @livewireScriptConfig + Vite—see docs for the exact layout variants.

2) Add NativePHP (Electron) and bootstrap it

composer require nativephp/electron
php artisan native:install

The installer publishes config/nativephp.php, registers the provider, and sets up the runtime. Run the app in a native window with:

php artisan native:serve

(You can use -vv for verbose logs during development.)

3) Create a tiny Livewire component

php artisan make:livewire NativeNotifier

We’ll wire this component to a route so it renders as a full page (Livewire 3 supports routing directly to a component class).

4) Drop in the files (FULL FILES)

Paste these in as-is. If a file already exists in your app, replace its contents with the version below.

<?php

declare(strict_types=1);

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Native\Laravel\Facades\Window;

class NativeAppServiceProvider extends ServiceProvider
{
    /**
     * Executed once the native application has been booted.
     * Use this method to open windows, register global shortcuts, menus, etc.
     */
    public function boot(): void
    {
        // Open a single app window and point it at our route.
        Window::open()
            ->title(config('app.name', 'Laravel + NativePHP'))
            ->width(1100)
            ->height(740)
            ->centerOnScreen()
            ->url(route('native-notify'));
    }
}

(This provider is published by native:install; we’re just giving it a sensible default window.)

<?php

declare(strict_types=1);

namespace App\Livewire;

use Livewire\Component;
use Native\Laravel\Facades\Notification;

class NativeNotifier extends Component
{
    public string $title = 'Hello from NativePHP';
    public string $message = 'This is a desktop notification from your Laravel app.';

    public function send(): void
    {
        // Trigger a real OS notification (NativePHP desktop).
        Notification::title($this->title)
            ->message($this->message)
            ->show();

        // Optional: In Livewire 3, browser events were unified into `dispatch()`.
        // If you want to react on the page, you might do:
        // $this->dispatch('notify-sent');
    }

    public function render()
    {
        return view('livewire.native-notifier');
    }
}

(NativePHP’s Notification facade shows true system notifications.)

<div style="max-width: 620px; margin: 2rem auto; font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;">
    <h1 style="font-size: 1.5rem; font-weight: 700; margin-bottom: .75rem;">
        Native Notification (Livewire 3 + NativePHP)
    </h1>

    <p style="margin-bottom: 1rem; color: #6b7280;">
        Click the button to show a real OS notification. Run the app with
        <code>php artisan native:serve</code>.
    </p>

    <div style="display: grid; gap: .75rem; margin-top: 1rem;">
        <label style="font-weight: 600;">Title</label>
        <input
            type="text"
            wire:model.live="title"
            placeholder="Notification title"
            style="padding: .6rem .8rem; border: 1px solid #e5e7eb; border-radius: .5rem;"
        >

        <label style="font-weight: 600; margin-top: .5rem;">Message</label>
        <textarea
            wire:model.live="message"
            rows="3"
            placeholder="Notification body"
            style="padding: .6rem .8rem; border: 1px solid #e5e7eb; border-radius: .5rem; resize: vertical;"
        ></textarea>

        <button
            wire:click="send"
            style="
                margin-top: .75rem;
                padding: .7rem 1rem;
                border-radius: .6rem;
                border: 1px solid #111827;
                background: #111827;
                color: white;
                font-weight: 600;
                cursor: pointer;
            "
        >
            Show Native Notification
        </button>
    </div>

    @script
    <script>
        // Optional Livewire-3 event example (unified event system).
        // Listen for `$this->dispatch('notify-sent')` from the component:
        Livewire.on('notify-sent', () => {
            console.log('Notification dispatched');
        });
    </script>
    @endscript
</div>

Livewire 3 no longer uses dispatchBrowserEvent()—the unified way is $this->dispatch('event-name') in PHP and Livewire.on('event-name', ...) in JS, or $dispatch in the template.

<?php
//web.php
use Illuminate\Support\Facades\Route;
use App\Livewire\NativeNotifier;

Route::get('/native-notify', NativeNotifier::class)->name('native-notify');

Route::get('/', function () {
    return redirect()->route('native-notify');
});

(Routing a page directly to the component class is idiomatic Livewire 3.)

//app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>{{ $title ?? config('app.name', 'Laravel') }}</title>
    </head>
    <body>
        {{ $slot }}
        {{-- Livewire auto-injects assets when a component is on the page.
             If you prefer manual/ESM control, see the installation docs. --}}
    </body>
</html>

(This mirrors Livewire’s quickstart layout—simple and works out of the box. You can switch to manual asset modes later if needed.)

5) Run it

  1. Start NativePHP: php artisan native:serve

  2. The native window opens on /native-notify (we set that in the provider).

  3. Click Show Native Notification → you’ll get a real OS notification.

Livewire-3 notes (quick refresher)

  • Unified events: emit() and dispatchBrowserEvent() (v2) are gone; use $this->dispatch('event-name', key: 'value') and listen with Livewire.on(...), or $dispatch inside templates.

  • Assets: Livewire auto-injects by default; you can opt into manual tags or ESM bundling with @livewireScriptConfig if integrating with custom Alpine plugins, etc.

Using the Livewire Starter Kit (Flux UI)

Starting from the Laravel Starter Kit → Livewire option gives you a modern app shell with Flux UI components prewired (Tailwind v4, auth scaffolding, settings, etc.). If you did that, you can swap the button to Flux like so:

<x-flux::button wire:click="send" appearance="primary">
    Show Native Notification
</x-flux::button>

(The rest of the files remain the same.)

Why this approach?

  • No JS glue for notifications: we call a PHP facade that talks directly to the OS via NativePHP.

  • Livewire 3 ergonomics: simple route-to-component, unified events, optional ESM.

  • Easy to extend: Add menus, dialogs, global shortcuts, or alerts (Alert facade) without leaving PHP.

Sources & further reading

  • NativePHP – Installation & Notifications (desktop v1).

  • Livewire 3 – Installation, Quickstart & Events (routing to components, asset strategy, unified dispatch).

  • Laravel Starter Kits (Livewire option ships with Flux UI).

That’s it—you’ve got a clean, minimal NativePHP + Livewire 3 seed that shows a real desktop notification. From here, you can add Flux UI modals, menus, or even hook notification clicks to open specific windows or routes.