Skip to main content

Ruby on Rails installation

Rails Testimonial Widget with Turbo Drive

In a Rails app the embed belongs in a partial you render from any view. The part that trips people up is Turbo Drive: it swaps page bodies without a full load, so where the script tag sits decides whether the widget fills on the second visit as well as the first.

  • An ERB partial that takes the widget id as a local
  • A per-view switch for a full reload under Turbo
  • The script_src entry for your CSP initializer

ERB generator

A partial, a render call and two optional extras

Type your widget id and the render call picks it up. Parts 1 and 2 are the install. Part 3 is only for a view where Turbo leaves the widget empty, and Part 4 only applies if your app already ships a Content Security Policy.

Shown in StoryProof when you copy a widget’s embed code.

Part 1 · The shared partial

app/views/shared/_storyproof_widget.html.erb

<%# app/views/shared/_storyproof_widget.html.erb %>
<%# The div stays empty in your markup; embed.js fills it in the browser. %>
<div data-storyproof-widget="<%= widget_id %>"></div>
<script src="https://app.storyproof.io/embed.js" async></script>

Part 2 · Rendering it in a view

app/views/<controller>/<action>.html.erb

<%# Any view, e.g. app/views/pages/home.html.erb %>
<section class="customer-proof">
  <h2>What our customers say</h2>
  <%= render partial: "shared/storyproof_widget", locals: { widget_id: "YOUR_WIDGET_ID" } %>
</section>

Part 3 · Optional: force a full load for one view

Layout head plus content_for :head in the view

<%# app/views/layouts/application.html.erb, inside <head> %>
<%= yield :head %>

<%# Only in a view where the widget misbehaves after Turbo visits %>
<% content_for :head do %>
  <meta name="turbo-visit-control" content="reload">
<% end %>

Part 4 · Optional: allow the script host in your CSP

config/initializers/content_security_policy.rb

# config/initializers/content_security_policy.rb
Rails.application.configure do
  config.content_security_policy do |policy|
    # Keep your existing sources and add the StoryProof host.
    policy.script_src :self, :https, "https://app.storyproof.io"
  end
end

Start the server, open the page, then click away to another route and back without refreshing. If the widget fills both times, Turbo is happy with it; if the second visit is blank, add Part 3 to that view.

The widget id only fills these templates on this page. It is not stored or sent.

The problem, solved

What goes wrong with embeds in a Turbo app, and the StoryProof side of the fix

  • Problem 1

    Blank widget after clicking a nav link

    Turbo Drive fetches the next page and swaps its body in. A script that only ran in the head on the first full load never sees the new div.

  • Problem 2

    Three partials loading the same script

    Pricing, home and a case study page each render the partial, and on a page with two widgets the script tag appears twice.

  • Problem 3

    Every new quote becomes a deploy

    Testimonials hard coded in a YAML file or a seed mean a pull request each time sales forwards a nice email.

  • Problem 4

    I do not want another table and admin

    A testimonials gem means migrations, an upload path for video and a moderation screen that somebody has to maintain.

In StoryProof

Ship it in a Rails app

Step 1 of 5

Fill the widget in StoryProof

Approve the testimonials you want, then choose which of them this widget shows and copy its widget id.

Where to put the script tag in a Rails layout

Turbo behaviour as described in the Turbo handbook, read 2026-11-13.
PlacementWhat Turbo does with itUse when
Inside the partial, in the bodyEvaluated each time Turbo renders a page that contains itDefault choice; widget pages are reached by in app links
Layout head, every pageLoaded on the first full load; Turbo merges the head on later visits rather than rerunning itAvoid for this embed unless you have confirmed widgets still fill after a Turbo visit
Partial plus reload meta for that viewTurbo hands that view to a normal browser loadA page where the widget is blank after a Turbo visit
Hosted Wall of Love linkAn ordinary link out of the appYou want proof without touching the views at all

Before you start

Limits in a Rails app

  • Repeat evaluation is not documented by StoryProof

    Turbo re-runs body scripts on each render. StoryProof has not published how embed.js behaves when evaluated again on the same tab, which is why this guide tells you to test a Turbo visit and keeps the reload fallback.

  • Turbo cache previews

    On some visits Turbo shows a cached copy of a page before the fresh one arrives, so the widget area can briefly look different from the final render.

  • No gem, no engine

    StoryProof is not a Ruby gem and adds no models or routes to your app. It is the embed in a partial, and the testimonials stay in StoryProof.

  • Badge on lower plans

    Widgets carry a small "Powered by StoryProof" badge until Pro.

  • Docs as of 2026-11-13

    The Turbo and Rails behaviour here follows the Turbo handbook and the Rails guides as read on 2026-11-13. These steps have not yet been run in a real Rails app by the StoryProof team.

FAQ

Questions people ask

Short answers to what usually comes up next.

Can I use javascript_include_tag for embed.js?

It returns a script tag for a source you give it, including an external URL, so it can produce the tag. Keep that tag in the body partial rather than the layout head, for the Turbo reasons above, and keep the async attribute.

Do I need Stimulus for this?

No. The embed is plain HTML and one script. A Stimulus controller is only worth writing if you have confirmed a specific Turbo problem the partial and reload fallback do not solve.

Will it work if Turbo is switched off?

Yes. Without Turbo Drive every navigation is a full page load, so the partial behaves like the embed on any static HTML page.

Can logged in customers leave a testimonial from inside the app?

Link to your StoryProof collection link from an account page or a post onboarding screen. The customer records or writes in the browser and the result waits for your approval.

Is there a no code option for the marketing team?

Every plan includes a hosted Wall of Love page with its own link. Linking to it needs no view changes at all.

Still have a question? Ask the StoryProof team or book a demo.

Get a widget id for your partial

Collect testimonials, approve the strongest and render one partial in your Rails views.

Sources

  1. Building Your Turbo Application, Turbo Handbook (Hotwire). Checked 2026-11-13.
  2. Navigate with Turbo Drive, Turbo Handbook (Hotwire). Checked 2026-11-13.
  3. Layouts and Rendering in Rails, Ruby on Rails Guides. Checked 2026-11-13.
  4. Securing Rails Applications: Content Security Policy, Ruby on Rails Guides. Checked 2026-11-13.

Last updated 2026-11-13.