← All posts
Guides7 min read

[GUIDES] · Jul 8, 2026 · 14:10

How to add live chat to a Rails app (without fighting Turbo)

One snippet in application.html.erb, a clear explanation of what Turbo Drive does to head scripts, and an AI that answers from your docs. About five minutes of real work.

Tm

The muro team

muro.chat

#rails#ruby#live chat#turbo drive#install guide#widget

Your Rails app is live and support is scattered: a Gmail thread here, a Twitter DM there, a reply to a transactional email that went to spam. Live chat fixes that, and on Rails the install is genuinely five minutes. But there is one thing worth understanding before you paste anything: Turbo Drive. It changes how scripts in your layout behave, and it is the reason plenty of third-party widgets act weird on Rails apps. This guide installs the muro widget in your layout, explains exactly what Turbo does with it, and then turns on AI replies grounded in your own docs.

What you are installing

muro is a live chat plus AI support inbox built for indie hackers and small teams. The widget is a ~6 KB async script, so it will not drag your Lighthouse score down, and the loader is idempotent, which matters more than you would think on a Turbo app (more on that in a minute). Behind the bubble you get a shared inbox, saved replies, automation rules, email forwarding with reply-by-email, and an AI that answers from your published help articles and hands off to a human when it is unsure. You can poke at the whole thing on the live demo before touching your codebase.

Step 1: create a site and grab your widget ID

  1. 01Create a muro account (14-day trial, no card needed) and add your Rails app as a site.
  2. 02Set the domain the widget will run on, for example app.example.com.
  3. 03Copy the widget ID from the installation screen. It is a short string you will paste into the snippet below.

Step 2: paste the snippet into your layout

Open app/views/layouts/application.html.erb and add this just before the closing </head> tag:

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 your real ID, deploy, and the launcher shows up in the corner of every page rendered with this layout. The script tag is created with async, so it never blocks rendering, and at roughly 6 KB the widget is smaller than most hero images.

The Turbo Drive part, honestly

Since Rails 7, Turbo Drive is on by default. When someone clicks a link, Turbo intercepts it, fetches the next page in the background, swaps the <body>, and merges the <head>. There is no full page reload. That has two consequences for any script sitting in your head:

  • It executes once, on the first real page load. Turbo leaves existing head elements alone on subsequent visits, so the script is not re-executed as users navigate around your app.
  • Whatever the script attached to window persists across visits, because the JavaScript context never resets.

This is exactly the scenario where a lot of chat widgets fall apart on Rails. Some vanish after the first navigation. Others get pasted into a turbo:load handler as a workaround and then initialize twice, which is how you end up with two launchers stacked on top of each other.

The muro snippet is safe here by design. Look at the loader line: w[o]=w[o]||function(){...}. If the snippet ever runs a second time, that guard turns the second run into a no-op, because the window.muro function already exists. So the placement Turbo encourages is also the placement muro wants: the script loads once in the head, keeps its state on window, and keeps working across Turbo visits. No turbo:load listener, no manual re-init, no duplicate widgets.

Where data-turbo-track actually fits

Your default layout probably tags your own bundles with data-turbo-track="reload". That attribute tells Turbo to watch an asset's fingerprint and force a full page reload when it changes, which is exactly what you want for application.js and application.css after a deploy, since Turbo would otherwise keep running stale code. Do not add it to the muro snippet. The widget script lives at a stable URL and updates on muro's side, so tracking it would only buy you pointless full page reloads.

A quick sanity check after deploying

  • Click around your app: the launcher should persist across navigation, and an open conversation should stay open while the visitor moves between pages.
  • Look for duplicates: two launchers means the snippet is rendered twice, usually once in the layout and once in a shared partial. Keep it in exactly one place per layout.
  • Ignore Turbolinks-era advice: older blog posts tell you to re-initialize widgets on turbolinks:load or turbo:load. With muro that is unnecessary, and skipping it is the whole point of the idempotent loader.
  • On Rails 5 or 6 with Turbolinks the story is the same: the guard makes any second execution harmless, so one head snippet is still all you need.

Keep the widget out of your admin

You do not want the customer chat bubble following you around your own admin panel. The clean Rails answer is a separate layout, not if statements wrapped around the snippet.

ruby# app/controllers/admin/base_controller.rb
class Admin::BaseController < ApplicationController
  layout "admin"
end

Create app/views/layouts/admin.html.erb without the snippet and inherit every admin controller from that base. Public pages get chat, staff pages stay clean. The same trick covers other places the widget should not appear: iframe endpoints, embedded views, a status page.

Customize it from the dashboard, not the code

Everything visual is configured inside muro, so tweaks never need a deploy: the accent color, the launcher position and size, the agent name and avatar visitors see, and the widget language. There are 12+ languages built in, so if your Rails app serves a non-English audience, the widget can match it. Change a setting, refresh, done.

Turn on AI replies grounded in your docs

The snippet gets you a chat bubble. Without this next step, the bubble is just a faster way to interrupt your deep work. The goal is having the first response handled before you even see the conversation:

  1. 01Import a URL. Point muro at your docs or FAQ page and it ingests your published articles as grounding material for the AI.
  2. 02Add product context: a short description of what your app does, your plans, your limits, anything a good support person would know.
  3. 03Enable auto-reply.

The important part is what the AI refuses to do. It answers the questions your docs actually answer. When someone asks for a refund, hits an account-specific problem, or is clearly frustrated, it hands the conversation to a human instead of improvising. That handoff behavior is what makes it safe to leave running overnight while you sleep.

Answer from wherever you already are

You are not going to live in another dashboard, so muro meets you in email. Forward your support address into muro and every conversation, chat or email, lands in one shared inbox. Reply-by-email means you can answer from your mail client and the response lands back in the conversation for the visitor. Add saved replies for the answers you type every week, and automation rules to tag and route what comes in.

If you want to wire support deeper into your Rails app, the full REST API and webhooks are there: create conversations from your own code, pull transcripts into your Postgres, or ping yourself when a conversation escalates to a human.

What it costs

Two plans, priced by project count instead of seats: Solo is $19/mo for up to 2 projects, Fleet is $59/mo for unlimited projects, and both include unlimited agents, so adding a co-founder or a contractor costs nothing extra. Managed AI credits are included (1,000/mo on Solo, 3,000/mo on Fleet, where 1 credit = 1 AI action), and if you would rather skip credit math entirely, paste your own Claude (Anthropic) API key: BYOK is unlimited at zero markup. Full details on the pricing page.

✦ ✦ ✦

The whole setup is one paste into application.html.erb and a coffee's worth of dashboard clicks. Create a site, drop the snippet before </head>, point the AI at your docs URL, and the next time a visitor asks how billing works, the answer is already sent before you have switched tabs.

✦ Try it

One support inbox for all your projects, one flat price.

muro is live chat, an AI that answers from your own docs, and a shared inbox for every site you run. See it both sides in the live demo, check the flat pricing, or see how muro compares. It's the same snippet on every platform — browse every install guide.

Tm

✎ Written by

The muro team

muro.chat