You shipped a Laravel app. Support right now means a contact form that fires a Mailable into your personal inbox, or customers replying to their Stripe receipt and hoping a human reads it. You want real live chat on the site, but every Laravel live chat tutorial wants you to install a WebSocket server, configure broadcasting, migrate a messages table, and then build an agent dashboard on top. That is a side project, not a support channel. Here is the ten minute version instead: one script tag in your Blade layout, an AI that answers from your docs, and email fallback for when you are heads down.
Build it with Reverb, or embed it?
Laravel makes real-time chat look temptingly buildable. Reverb is a first party WebSocket server, Echo handles the browser side, and most Laravel live chat tutorials walk you through wiring the two together. As a learning project, great. As a support channel, the socket is maybe 10 percent of the work. The other 90 percent is everything around it:
- →an inbox UI where you read, assign, and answer conversations
- →visitor identification and message persistence across sessions
- →notifications for when you are away from the app (which is always)
- →email fallback for when the visitor closes the tab before you reply
- →spam handling and all the abuse cases you have not thought about yet
If chat is your product, build it with Reverb and enjoy every minute. If chat is just how customers reach you, embed a hosted widget and get back to your actual roadmap. The rest of this guide is the embed path with muro: a roughly 6 KB async widget on the front, a shared inbox with AI on the back. No composer package, no npm install, no Vite config, no build step.
Step 1: grab your widget ID
Sign up for muro (14-day free trial, no card needed), create a site for your app, and copy the widget ID from the install screen. While you are in there, set the widget color, position, agent name, and avatar so it does not look like a foreign object bolted onto your design. The widget speaks 12+ languages, so if your Laravel app serves a non-English audience, pick the language there too.
Step 2: paste the snippet into your Blade layout
The whole install is one edit. Open your main layout. On a classic setup that is resources/views/layouts/app.blade.php, the file your pages extend with @extends('layouts.app'). On newer starter kits built around anonymous components (Livewire 3 defaults to this) it lives at resources/views/components/layouts/app.blade.php. The right file is whichever one actually contains your </head> tag. Paste this right before it:
html<script>
(function(w,d,s,o){
w.MuroChat=o;w[o]=w[o]||function(){(w[o].q=w[o].q||[]).push(arguments)};
var j=d.createElement(s);j.async=1;j.src='https://muro.chat/widget.js';
d.getElementsByTagName('head')[0].appendChild(j);
})(window,document,'script','muro');
muro('init',{widgetId:'YOUR_WIDGET_ID'});
</script>Replace YOUR_WIDGET_ID with the ID from step 1, deploy, done. Every page that extends or wraps that layout now has chat: the marketing pages, the dashboard, the billing screens, everything. The script loads async, so it does not block rendering and your Lighthouse numbers stay where they were.
The tidy alternative: Blade stacks
Some people (reasonably) do not want vendor scripts inlined in the middle of their layout markup. Blade stacks give you one clean mounting point instead. Add the stack to your layout once:
html{{-- resources/views/layouts/app.blade.php --}}
<head>
...
@stack('scripts')
</head>Then push the snippet from a dedicated partial, something like resources/views/partials/chat.blade.php, with the snippet wrapped in @push('scripts') ... @endpush. Now the widget lives in exactly one file. When you swap widget IDs between staging and production there is a single place to look, and you can go one step further by pulling the ID from config: muro('init',{widgetId:'{{ config('services.muro.widget_id') }}'}), with the value set per environment in .env.
Keep chat off your admin routes
You probably do not want the support bubble following you around your own admin panel, and you definitely do not want to invite chats on pages a visitor should never see. Wrap the snippet (or the stack push) in a simple @if:
html@if (! request()->routeIs('admin.*'))
{{-- the muro snippet from step 2 --}}
@endifrequest()->routeIs('admin.*') matches any named route in your admin group. If your admin routes are not named, match on the URL path instead with request()->is('admin/*'). The same trick works in every direction: wrap the snippet in @auth ... @endauth if chat should only exist for logged-in customers, or @guest if it is purely a pre-sales tool on the marketing pages.
Using Inertia or Livewire?
Same move, slightly different file. Inertia apps still serve their </head> from Blade through the root template at resources/views/app.blade.php. Paste the snippet there and it survives client-side navigation, because Inertia never does a full page reload. Livewire 3 full-page components render into resources/views/components/layouts/app.blade.php by default. Either way, you install once and forget it.
Now make it answer without you
Installing the widget is the easy 10 percent, same as the socket would have been. The reason to do any of this is what happens after a visitor types. In the muro dashboard, point the AI at your existing documentation: paste the URL of your docs or FAQ page and it imports the content. From then on, the AI answers questions grounded on your published help articles and product context. Not a generic chatbot doing improv, an assistant that repeats what you actually wrote.
- →your public docs or help center URL (import once, import again when things change)
- →your FAQ page, especially the pricing and billing questions
- →product context: what the app does, which plans exist, where the limits are
The part that matters for trust: when the AI is not sure, it hands the conversation to you. Refund requests, account and billing problems, and visitors who are clearly frustrated all get escalated to a human instead of hallucinated at. For a solo Laravel dev this is exactly the right split. The AI absorbs the how-do-I-reset-my-API-key traffic, and you handle the conversations where being a human is the feature.
Reply-by-email, for when you are deep in a migration
The failure mode of every live chat install is the abandoned bubble: a visitor writes, nobody answers, and the widget starts doing negative marketing for you. muro's answer for solo devs is email. Conversations the AI hands off get forwarded to your inbox, and you reply from your mail client like a normal person. Your reply lands back in the visitor's chat window as if you had been sitting in the dashboard the whole time. No extra tab open, no separate app to check.
Pair that with a few saved replies for the questions you answer weekly and an automation rule or two for the repeat patterns, and the whole support surface of your Laravel app runs on maybe fifteen minutes a day. If you later bring in a co-founder or a first hire, the shared inbox is already sitting there waiting, and muro has unlimited agents on every plan (no per-seat pricing), so the extra pair of hands costs nothing extra.
And since you clearly like building things, none of this is a black box. muro exposes a full REST API and webhooks with per-site scoped keys, so you can pipe new conversations into your own admin panel, a Slack channel, or anywhere else PHP can reach. The API docs cover all of it, and there is a hosted MCP server if you want to read conversations from your editor.
Ship it
That is the whole job: create a site in muro, paste the snippet into your layout before </head>, wrap it in an @if to skip the admin, and point the AI at your docs. Ten minutes of work for a support channel that answers questions at 3 a.m. without waking you. Poke at the widget yourself in the live demo, then go make the edit to app.blade.php.
