Gatsby installation
Gatsby Testimonial Widget with Script API
Gatsby ships its own Script component, added in gatsby 4.15.0, and it is the right home for the StoryProof embed.js file. Wrap it with the widget div in a small React component, render that component on the page that should carry proof, and Gatsby loads the script after hydration. Because Script deduplicates by src, using the wrapper twice on one page still fetches the file only once.
- A wrapper built on Gatsby’s Script component
- Rendering it from a page in src/pages
- Why the link into that page matters
Gatsby code
Wrapper, page and the link into it
Type your widget id and the three snippets fill in. The first is a reusable component, the second renders it on a page under src/pages, and the third shows how to link to that page.
Part 1 · Step A: the wrapper component
src/components/storyproof-widget.tsx
// src/components/storyproof-widget.tsx
import * as React from "react"
import { Script } from "gatsby"
type Props = { id: string }
export function StoryProofWidget({ id }: Props) {
return (
<>
<div data-storyproof-widget={id} />
{/* Gatsby skips a second copy with the same src */}
<Script src="https://app.storyproof.io/embed.js" />
</>
)
}Part 2 · Step B: render it on a page
src/pages/customers.tsx
// src/pages/customers.tsx
import * as React from "react"
import { StoryProofWidget } from "../components/storyproof-widget"
export default function CustomersPage() {
return (
<main>
<h1>Customer stories</h1>
<StoryProofWidget id="YOUR_WIDGET_ID" />
</main>
)
}Part 3 · Step C: the link into that page
Any component or Markdown template that links to it
// Linking to that page from elsewhere on the site
// A plain anchor asks the browser for a fresh page load.
<a href="/customers/">Read customer stories</a>
// Gatsby's <Link to="/customers/"> swaps the page on the client,
// and StoryProof does not claim the widget fills after that.Run gatsby build followed by gatsby serve, open /customers/ directly in the browser, and confirm the testimonials appear. Then click through to it from the homepage to see how your own navigation behaves.
The snippets are put together in your browser. Nothing you type is stored.
The problem, solved
Gatsby specific snags and how this wrapper handles them
- Problem 1
The vendor script tag does nothing
React does not execute a lowercase script element rendered in JSX, so a copied snippet sits in the page as dead markup.
How StoryProof solves it
The StoryProof embed is an empty div plus one async script, so the div renders as JSX and Gatsby’s Script component loads the file.
- Problem 2
Two sections, two copies of the script
A homepage with a carousel near the top and a wall near the footer would mount the wrapper twice.
How StoryProof solves it
Gatsby deduplicates Script by src, and one embed.js on the page fills every StoryProof div on it, so each wrapper only adds its own div.
- Problem 3
Quotes hard coded into GraphQL data
When testimonials live in Markdown or JSON files queried through GraphQL, swapping one means a commit and a rebuild.
How StoryProof solves it
The page holds a widget id. Which approved testimonials that widget shows is picked in StoryProof rather than in your source files.
- Problem 4
Widget empty after clicking a Link
Gatsby’s Link swaps pages on the client, and the script on a page reached that way does not get a fresh page load.
How StoryProof solves it
Link to the testimonial page with a plain anchor so the browser loads it in full; the embed is then the same div and script it always is.
- Problem 5
Video clips bloating the static folder
Customer videos committed to the repository make every build and deploy heavier.
How StoryProof solves it
Customers record video, an audio note or a written review through one StoryProof link in their browser, so the files never enter your repo.
In StoryProof
Install it in a Gatsby project
Step 1 of 4
Copy the widget id from StoryProof
Choose the layout and which approved testimonials the widget holds. The id is the value of data-storyproof-widget in its embed code.
Step 2 of 4
Add the wrapper component
Save Step A under src/components. It imports Script from gatsby and leaves the strategy at the default, post-hydrate, which loads the file after the page hydrates.
Step 3 of 4
Render it on the page
Import the wrapper into a page in src/pages, or into a template, and pass the widget id as a prop.
Step 4 of 4
Check the links that lead there
Where a menu or button points at the testimonial page, use a plain anchor as in Step C, then build, serve and click through to confirm.
Which Script strategy suits embed.js
Before you start
What to know before you ship
Gatsby docs as of 2026-11-14
The Script import, strategies, deduplication and wrapPageElement notes follow gatsbyjs.com as read on 2026-11-14. Nobody has built these snippets on a live Gatsby site yet.
Client side navigation
StoryProof does not claim a widget fills itself after a Gatsby Link transition. Plain anchors into the testimonial page avoid the question; test any other route in.
No Gatsby plugin
StoryProof has no Gatsby plugin or theme. The wrapper above is the whole integration, and it lives in your repo.
Content Security Policy
If your host sends a CSP header, app.storyproof.io has to be allowed in script-src or the browser refuses the file.
Badge until a paid plan
Widgets show a small "Powered by StoryProof" badge until Pro.
FAQ
Questions people ask
Short answers to what usually comes up next.
Can I use the idle strategy instead?
Yes. Add strategy="idle" to the Script element and Gatsby waits until the main thread is free. Scroll to the widget on a slow phone afterwards to check it is ready when a reader gets there.
Why not off-main-thread, since it keeps scripts off the main thread?
Gatsby marks it experimental, it needs the script URL listed in partytownProxiedURLs, and its docs say those scripts load only on server rendered navigation. The default strategy is simpler here.
Can the script load once for the whole site instead?
Yes. Gatsby documents Script inside wrapPageElement, and recommends writing that wrapper in both gatsby-browser and gatsby-ssr. Keep only the div in the component if you go that way.
Will a second wrapper on the page load embed.js again?
No. Gatsby uses the src value to deduplicate Script, and one copy of embed.js fills every StoryProof div on the page.
Still have a question? Ask the StoryProof team or book a demo.
Get a widget id for your Gatsby build
Collect and approve testimonials in StoryProof, then pass the id to one small component.