Skip to main content

Laravel installation

Laravel Blade Testimonial Widget Embed

In a Laravel app the cleanest home for the StoryProof embed is an anonymous Blade component. It renders the div from an id prop and pushes the script onto a named stack with @pushOnce, so however many times a view uses the component, the layout’s @stack prints embed.js a single time. No package to install, no database table for quotes.

  • An anonymous component with an id prop
  • @pushOnce so the script is never duplicated
  • The @stack line your layout needs

Blade snippets

Component, layout stack and usage

Enter a widget id. Snippet 1 is the anonymous component. Snippet 2 is the line your layout needs before the body closes. Snippet 3 shows the component in a view, with the id passed as an attribute.

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

Part 1 · Snippet 1: the component

resources/views/components/storyproof-widget.blade.php

@props(['id'])

<div {{ $attributes }} data-storyproof-widget="{{ $id }}"></div>

@pushOnce('scripts', 'storyproof-embed')
    <script src="https://app.storyproof.io/embed.js" async></script>
@endPushOnce

Part 2 · Snippet 2: render the stack in the layout

resources/views/layouts/app.blade.php (or components/layout.blade.php), before </body>

    @stack('scripts')
</body>
</html>

Part 3 · Snippet 3: use it in any view

resources/views/pricing.blade.php

<section class="testimonials">
    <h2>Teams that switched</h2>
    <x-storyproof-widget id="YOUR_WIDGET_ID" class="mt-8" />
</section>

Load the route in the browser and view source. You should find the div inside the section and one embed.js tag at the end of the body, even if the view uses the component twice.

Your id is only used to fill in the snippets on this page.

The problem, solved

What goes wrong in Laravel views, and the fix

  • Problem 1

    The script printed twice on one page

    A partial that carries its own script tag repeats it every time a view includes the partial.

  • Problem 2

    A testimonials package means migrations

    Packages add tables, models and an admin form just to show a few quotes.

  • Problem 3

    Different praise on different routes

    One hard coded block of quotes repeats the same three customers on every page.

  • Problem 4

    Building upload handling for video

    Accepting customer video in your own app means storage, encoding limits and moderation code.

In StoryProof

Add it to a Laravel app

Step 1 of 4

Create the component

Save Snippet 1 as resources/views/components/storyproof-widget.blade.php. Any Blade file in that folder works as an anonymous component; @props declares id as a prop.

Blade tools for the script line

DirectiveWhat it does hereUse it?
@pushAdds the script every time the component rendersNo: duplicates with two widgets
@pushOnce with an identifierAdds it once per render, even from two componentsYes
@stackPrints everything pushed to the named stackYes, once in the layout
@prependPuts content at the start of a stackOnly if another script must load after it

Before you start

Boundaries of this guide

  • Laravel docs as of 2026-11-13

    Stacks, @pushOnce and anonymous components follow the Laravel 12 Blade docs read on 2026-11-13; Laravel 13 is current and was not checked separately. Not run in a live app.

  • Blade only, not Inertia

    Apps whose pages are Vue or React through Inertia render views in JavaScript. Place the widget in those components instead; see the React and Next.js guide.

  • Content Security Policy

    If middleware sends a CSP header, allow app.storyproof.io in script-src, or the browser blocks the script.

  • No Laravel package

    StoryProof does not publish a Composer package for Laravel. The component is your own code.

  • Badge on lower plans

    A small "Powered by StoryProof" badge appears under widgets until Pro.

FAQ

Questions people ask

Short answers to what usually comes up next.

My layout uses @yield, not components. Does this still work?

Yes. Stacks work with either style of layout. Put @stack('scripts') before the closing body tag of the layout your views extend.

Why does @pushOnce take a second argument?

The identifier lets Blade recognise the same push coming from different templates, so the script is rendered once even if two different components push it.

Can I load the script with Vite instead?

Keep it as a plain script tag with the src exactly as given. Bundling a copy would stop it receiving changes made to the hosted file.

Does the widget work in Livewire components?

The div can sit in a Livewire view, but this guide does not cover Livewire page navigation. Test the widget after navigating between pages, not only on a full load.

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

Skip the testimonials table

Collect and approve in StoryProof, then render one Blade component.

Sources

  1. Blade Templates (Stacks, The @once Directive, Anonymous Components), Laravel Documentation (12.x). Checked 2026-11-13.

Last updated 2026-11-13.