bangnokia/laravel-serve-livereload
Minimal steps
composer require bangnokia/laravel-serve-livereload --dev
php artisan vendor:publish --tag=laravel-serve-livereload # creates config/livereload.php
php artisan serve
That's the whole loop: the package wraps artisan serve with a sidecar WebSocket server and injects a small client script into the HTML responses PHP's built-in server returns. Save a watched file, the browser reloads itself. No browser extension, no browser-sync, no gulp — which is the entire reason the package exists.
Where to look first
config/livereload.php — the only real surface area. Read it before you run anything; the defaults usually need at least a port and a watch-path decision for your project layout.php artisan serve — it should tell you the WebSocket server is listening on a port. If that line is missing, nothing else will work.<script> block near the end of <body>. If it's absent, the package isn't hooking the response.First use case
Classic Blade app on a single developer machine: edit resources/views/foo.blade.php or app/Http/Controllers/..., alt-tab back, page is already fresh. Start here and verify it works on a trivial view edit before touching config, because a broken setup and a mis-scoped watch path look identical from the browser.
Scope it to HTML, not the API
The injection is response-level, so decide early whether the package should be touching every route. If your app has JSON endpoints the injection is either a no-op or a hazard (see Gotchas). Common pattern: enable the package only for your local Blade-served surface, and let a separate Vite dev server handle anything SPA-ish.
Watch paths, deliberately
The watch list is the main tuning knob. A practical starting shape:
| Watch | Why |
|---|---|
app/ |
controllers, models, Livewire components — the real reload trigger |
resources/views/ |
Blade edits |
routes/ |
route changes (full reload is correct here) |
config/ |
config edits, though you still need a process restart for many of them |
lang/ |
translation tweaks |
Explicitly exclude vendor/, node_modules/, storage/, public/build/, and .git/. Recursive watching of a node_modules tree is the #1 cause of a laptop fan spinning up during artisan serve.
Pair it with your asset pipeline, don't replace it
Two workable patterns:
resources/js|css.public/css and public/js to the watch list so hand-edited assets also trigger reloads.Multi-project machines
You will have three Laravel apps open. Give each a distinct WebSocket port in its own config/livereload.php. There's no autodiscovery — a second app on the same port will silently fail to connect in the browser while the console still shows a server started, because the other project's server grabbed the port.
Sail / Docker
The WebSocket server binds inside the container. You need the port published in docker-compose.yml alongside the app port, and the injected client must connect to a host the browser can reach (usually localhost), not the container hostname. If the injected script hardcodes the request host and the request came in via localhost:8000, that's fine; if you access the app by container name from another machine, expect connection failures. For team projects, treat this package as a per-developer convenience rather than something baked into the compose file.
When to prefer something else
artisan serve runtime: this package's hook doesn't apply. Use Vite HMR, or browser-sync proxying your Valet URL.It only hooks artisan serve. That's the design, not a bug. php artisan serve uses PHP's built-in server, and the package intercepts responses there. Under Octane, FPM, Valet, or a real nginx box, no script is injected and no reload happens. Don't spend an hour debugging what is a scoping misunderstanding.
Injection can corrupt non-HTML responses. If the package appends a <script> to a JSON, XML, or file-download response, clients break in confusing ways — fetch() calls throwing parse errors, CSV downloads with HTML appended. If you hit this, narrow what the middleware/response hook touches, or point your API consumers at a route the package is configured to skip.
Content-Security-Policy will eat the client script. A strict script-src or connect-src in a local security-headers middleware silently blocks both the injected script and the WebSocket. Browser console shows a CSP violation — that's the tell. Loosen the policy in local only.
Port conflicts are the most common failure. Symptom: artisan serve starts, browser never reloads, no obvious error. Check with lsof -i :<port> (or netstat -ano | findstr on Windows) whether something already owns it. Pick a non-default port per project and write it down.
File-watch granularity varies by OS. Editor "safe writes" (write temp file, rename over target) can produce events that some watchers miss, and network drives / WSL-mounted paths (/mnt/c/...) are frequently unreliable. If edits don't trigger, test by touch-ing a file from the terminal. Terminal touch reloads but editor saves don't = it's a filesystem-events problem, not a package problem.
Editing config/livereload.php won't hot-apply. Config is read at artisan serve boot. Kill and restart the serve process — and remember that restarting the serve process doesn't reload the browser tab, so you'll still need one manual refresh.
Debugging order that actually works:
artisan serve output?lsof/netstat)<script> in view-source?Each step isolates a different layer. Jumping straight to "the package is broken" wastes the most time at step 3, which is almost always config or a response type it doesn't touch.
Package age is a real risk. Last release January 2023. On Laravel 10/11 you may hit signature changes in the serve command or in the response lifecycle it hooks, and there's no maintainer activity to lean on. Mitigations: keep it strictly in require-dev, expect to patch a fork if a Laravel upgrade breaks it, and don't let any of your app code depend on it. If it becomes friction, the fifteen minutes to set up Vite HMR (or a browser-sync proxy) buys you a maintained tool with CSS hot-swap — this package's core value is that it's zero-config for a plain Blade app on artisan serve, and that value evaporates the moment your setup stops looking like that.
Extension points are thin, so plan accordingly. The realistic levers are the published config (ports, watch/ignore paths) and, if needed, a small forked copy of the response-injection logic to add your own exclusions. There's no plugin API, no event hooks worth building on. Treat it as a convenience shim, not infrastructure.
How can I help you explore Laravel packages today?