Hugo installation
Hugo Static Site Testimonial Widget
Hugo’s Markdown renderer leaves raw HTML out of your content by default, so pasting the StoryProof div into a .md file quietly produces nothing. The Hugo way is a shortcode: a small template in layouts/_shortcodes that writes the div, called from Markdown with the widget id. The script goes in a partial your base template already includes, so every page loads it once.
- A storyproof shortcode for Markdown content
- A partial that loads embed.js once
- Why raw HTML in Markdown vanishes
Hugo templates
Shortcode, partial and the Markdown call
Enter a widget id. File 1 is the shortcode, File 2 the partial for the script, File 3 the one line to add in baseof.html, and the last block is what an author types in Markdown.
Part 1 · File 1: the shortcode
layouts/_shortcodes/storyproof.html
{{- $id := "" -}}
{{- if .IsNamedParams }}{{ $id = .Get "id" }}{{ else }}{{ $id = .Get 0 }}{{ end -}}
<div data-storyproof-widget="{{ $id }}"></div>Part 2 · File 2: the script partial
layouts/_partials/storyproof-script.html
<script src="https://app.storyproof.io/embed.js" async></script>Part 3 · File 3: include the partial before </body>
layouts/baseof.html (or your theme’s base template, copied into your project)
{{ partial "storyproof-script.html" . }}
</body>
</html>Part 4 · In any Markdown page
content/about.md
## What clients say
{{< storyproof id="YOUR_WIDGET_ID" >}}Run hugo server, open the page, and view source: the div should sit in the content and embed.js once near the end of the body. Then build with hugo and deploy as usual.
These templates are produced in your browser. The id is not stored.
The problem, solved
Hugo testimonial snags and how this setup avoids them
- Problem 1
My pasted HTML vanished from the page
Hugo’s Goldmark renderer skips raw HTML in Markdown unless markup.goldmark.renderer.unsafe is turned on, which Hugo describes as unsafe for content you do not control.
How StoryProof solves it
The shortcode writes the StoryProof div from a template, so Markdown stays safe and the widget still appears.
- Problem 2
Testimonials live in a YAML data file
Theme blocks read quotes from front matter or data files, so each new one is a commit and a rebuild.
How StoryProof solves it
The page holds one shortcode call. Which approved testimonials appear is chosen in StoryProof, so the selection changes without editing data files.
- Problem 3
Two shortcodes, two script tags
A shortcode that prints the script would load it again for every widget on the page.
How StoryProof solves it
The script lives in a partial called once from the base template. One embed.js load fills every StoryProof div on a page.
- Problem 4
Theme updates overwrite my edits
Editing files inside themes/ means losing changes when you pull a new theme version.
How StoryProof solves it
Put the shortcode and partial in your project’s layouts folder and copy only baseof.html out of the theme; the embed itself is just a div and a script.
- Problem 5
Getting video praise into a static site
Hosting customer clips in static/ bloats the repo and needs a player.
How StoryProof solves it
Customers record video, audio or text through one StoryProof link in their browser, and the widget shows what you approve.
In StoryProof
Add it to a Hugo project
Step 1 of 4
Create the shortcode
Save File 1 as layouts/_shortcodes/storyproof.html. It checks .IsNamedParams, then reads the id by name or as the first positional argument with .Get.
Step 2 of 4
Create the script partial
Save File 2 as layouts/_partials/storyproof-script.html. It holds only the async script line.
Step 3 of 4
Call it from the base template
In baseof.html, add the partial call just before the closing body tag. If the theme supplies baseof.html, copy it into your own layouts folder first so the theme stays untouched.
Step 4 of 4
Use it in content and build
Add the shortcode call to any Markdown page, run hugo server to check, then build and deploy the public folder as you normally do.
Where each piece lives in Hugo
Before you start
Limits worth knowing
Hugo docs as of 2026-11-13
Directory names, the .Get method, partial calls and the Goldmark setting follow gohugo.io as read on 2026-11-13. The templates have not been built on a live project.
Script on every page
Calling the partial from baseof.html loads embed.js on pages without a widget too. Wrap the call in a condition on a front matter flag if that matters to you.
Hosting headers
If your host adds a Content Security Policy, allow app.storyproof.io in script-src or the script is blocked.
No Hugo module or theme
StoryProof does not ship a Hugo module or theme component. The shortcode is yours to keep in the project.
Badge on lower plans
Widgets carry a small "Powered by StoryProof" badge until Pro.
FAQ
Questions people ask
Short answers to what usually comes up next.
Should I just turn on unsafe in the Goldmark settings?
You can, and the raw div would then render. A shortcode keeps the setting off, which Hugo recommends unless you control all content, and gives authors a cleaner line to type.
Do I need to rebuild when testimonials change?
Only when the page itself changes, such as adding the shortcode somewhere new. The selection of testimonials is managed in StoryProof.
Can I use positional arguments instead of id=?
Yes. The shortcode reads .Get 0 when the call uses positional arguments, so {{< storyproof abc123 >}} works too. Do not mix both styles in one call.
Does this work with Hugo themes like Hugo Blox?
Yes, as long as your project can add its own layouts. Themes that build pages from blocks may need the partial called from their own base template.
Still have a question? Ask the StoryProof team or book a demo.
Keep your Hugo site static and your testimonials current
Collect and approve in StoryProof, then drop one shortcode into Markdown.
Sources
- Shortcode templates, Hugo Documentation. Checked 2026-11-13.
- Shortcodes, Hugo Documentation. Checked 2026-11-13.
- partials.Include, Hugo Documentation. Checked 2026-11-13.
- Base templates, Hugo Documentation. Checked 2026-11-13.
- Configure markup, Hugo Documentation. Checked 2026-11-13.
Last updated 2026-11-13.