The embed in one sentence
The Tarvio widget is a plain JavaScript file. You load it once with a <script> tag, then place a <div data-tarvio="your-slug"> wherever you want the widget to render. That's the entire integration — the rest of this article is just how to fit those two pieces into each framework correctly.
<div data-tarvio="YOUR_WORKSPACE_SLUG"></div>
<script src="https://www.tarvio.io/widget.js" defer></script>
Your workspace slug is the short identifier shown in your Tarvio dashboard under the Embed tab — it's already pre-filled in the code snippet there.
Next.js — App Router
App Router is the default in Next.js 13+. The recommended pattern is to load the script once in your root layout and place the widget div wherever you need it.
Add the script to app/layout.tsx. Use Next.js's built-in Script component with strategy="afterInteractive" so it loads after the page shell is ready:
// app/layout.tsx
import Script from 'next/script'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Script src="https://www.tarvio.io/widget.js" strategy="afterInteractive" />
</body>
</html>
)
}
Then place the widget div in any Server or Client Component. TypeScript will warn about unknown HTML attributes — suppress it with a type cast or extend the JSX namespace:
// app/page.tsx
export default function LandingPage() {
return (
<section>
<h2>What our customers say</h2>
{/* TypeScript: cast to any to allow data-tarvio */}
<div {...({ 'data-tarvio': 'your-workspace-slug' } as any)} />
</section>
)
}
If you prefer a cleaner solution, declare the attribute once in a global type file and avoid the cast everywhere:
// types/jsx.d.ts
import type { HTMLAttributes } from 'react'
declare module 'react' {
interface HTMLAttributes<T> {
'data-tarvio'?: string
}
}
After adding that declaration, <div data-tarvio="your-slug" /> works in any component without casting.
The widget script uses a MutationObserver internally, so it detects the div even when it appears after initial load — client-side navigation in App Router works without any extra configuration.
Next.js — Pages Router
For projects still on Pages Router, load the script in _app.tsx using the same Script component:
// pages/_app.tsx
import type { AppProps } from 'next/app'
import Script from 'next/script'
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Component {...pageProps} />
<Script src="https://www.tarvio.io/widget.js" strategy="afterInteractive" />
</>
)
}
Then use the div in any page file. The same TypeScript declaration above applies here as well.
// pages/index.tsx
export default function Home() {
return (
<main>
<div data-tarvio="your-workspace-slug" />
</main>
)
}
Webflow
Webflow has two places to add custom code: site-wide (loads on every page) and per-page (loads only on that page). Use site-wide for the script tag and per-page for the div — or put both on the same page if you only need the widget in one place.
Site-wide script (recommended)
Go to Site Settings → Custom Code → Footer Code and paste the script tag. It loads on every page, so you only need to do this once:
<script src="https://www.tarvio.io/widget.js" defer></script>
Placing the widget on a page
Open the page in the Designer. In the Add panel, find Components → Embed and drag it where you want the widget. Paste the div into the code block:
<div data-tarvio="YOUR_WORKSPACE_SLUG"></div>
Click Save & Close, then publish. The widget renders in the published site (not in the Designer preview, which blocks external scripts).
Webflow CMS collection pages
If you want the widget on a CMS template page (e.g., every product page), place the Embed element inside the CMS collection list template. The same div works — the slug is fixed per workspace, not per CMS item, so it shows the same widget everywhere.
Framer
Framer has two ways to add the widget: using the built-in Embed component for a one-off placement, or creating a reusable Code Component if you want to drop it onto multiple pages.
Embed component (quickest)
On the canvas, press E or search for Embed in the Insert menu. Resize the frame to the height you want, then click Edit HTML and paste both lines:
<div data-tarvio="YOUR_WORKSPACE_SLUG"></div>
<script src="https://www.tarvio.io/widget.js" defer></script>
The widget sizes itself to its content, so the frame height only matters during editing — it expands at runtime. Publish the page for the widget to load (it does not execute inside the Framer canvas).
Reusable Code Component
If you want to reuse the widget across multiple pages without re-pasting, create a Code Component. In Framer, go to Assets → Code → New Component and replace the template with:
// TarvioWidget.tsx
import { useEffect } from "react"
export default function TarvioWidget({ slug }: { slug: string }) {
useEffect(() => {
if (document.querySelector('script[src*="widget.js"]')) return
const s = document.createElement("script")
s.src = "https://www.tarvio.io/widget.js"
s.defer = true
document.body.appendChild(s)
}, [])
return <div data-tarvio={slug} />
}
Drag TarvioWidget from the Assets panel onto any frame and set the slug prop to your workspace slug in the Properties panel. The useEffect guard prevents the script from being inserted more than once if you use the component on multiple pages in the same session.
Troubleshooting: widget not showing up
If you've placed the embed but see a blank space or nothing at all, check these in order:
- Wrong or missing slug. Open your browser's DevTools and check the Network tab for a request to
widget.js. If it's loading, look at the div in the Elements tab — it should havedata-tarvio="your-slug"with the correct value. A typo or empty attribute is the most common cause. - No approved testimonials. The widget only renders if your workspace has at least one approved testimonial. Log in to Tarvio and confirm there is at least one testimonial with an “Approved” status.
- Script blocked by CSP. If your site has a Content Security Policy, you may need to add
https://www.tarvio.ioto yourscript-srcandconnect-srcdirectives. - Webflow / Framer canvas preview. Both platforms block external scripts in the editor. Publish or preview the live URL to test.
- Script loaded before the div. If you're using a plain
<script>tag withoutdeferand it appears before the div in the HTML, the widget may initialise before the target element exists. Adddefer, or move the script tag to the bottom of<body>.
If you've checked all of the above and the widget still isn't showing, the free Tarvio account includes a live embed preview in the dashboard that confirms your slug is working correctly.