Add testimonial widgets to your Next.js app. Script tag embed or React component. SSR compatible. Minimal bundle impact.
Add our script tag and use anywhere. Zero dependencies. Works immediately.
Use our React component for better control and TypeScript support.
The fastest way to add Shoutjar to Next.js. Works with both App Router and Pages Router.
Add the script in your root layout:
import Script from 'next/script'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://cdn.shoutjar.com/widget.js"
strategy="lazyOnload"
/>
</body>
</html>
)
}Then add the widget HTML wherever you need it:
export default function Home() {
return (
<main>
<h1>Welcome</h1>
<div
data-shoutjar-widget="YOUR_WIDGET_ID"
data-shoutjar-style="carousel"
/>
</main>
)
}Add script in _app.tsx or _document.tsx:
import Script from 'next/script'
export default function App({ Component, pageProps }) {
return (
<>
<Component {...pageProps} />
<Script
src="https://cdn.shoutjar.com/widget.js"
strategy="lazyOnload"
/>
</>
)
}lazyOnloadLoads after page is interactive (recommended)afterInteractiveLoads immediately after hydrationbeforeInteractiveLoads before hydration (not recommended for widgets)For better control and TypeScript support, use our React wrapper.
npm install @shoutjar/react
# or
yarn add @shoutjar/react
# or
pnpm add @shoutjar/reactimport { ShoutjarWidget } from '@shoutjar/react'
export default function Testimonials() {
return (
<ShoutjarWidget
widgetId="YOUR_WIDGET_ID"
style="carousel"
/>
)
}import { ShoutjarWidget } from '@shoutjar/react'
export default function Testimonials() {
return (
<ShoutjarWidget
widgetId="YOUR_WIDGET_ID"
style="grid"
theme="dark"
maxItems={6}
showRating={true}
className="my-custom-class"
/>
)
}Full TypeScript support included:
import { ShoutjarWidget, WidgetStyle, WidgetTheme } from '@shoutjar/react'
interface Props {
style?: WidgetStyle // 'grid' | 'carousel' | 'marquee' | 'badge' | 'single'
theme?: WidgetTheme // 'light' | 'dark' | 'auto'
}
export default function Testimonials({ style = 'carousel', theme = 'auto' }: Props) {
return (
<ShoutjarWidget
widgetId={process.env.NEXT_PUBLIC_SHOUTJAR_WIDGET_ID!}
style={style}
theme={theme}
/>
)
}Shoutjar works seamlessly with Next.js server rendering, static generation, and ISR.
If using the React component in App Router, add 'use client':
'use client'
import { ShoutjarWidget } from '@shoutjar/react'
export default function Testimonials() {
return <ShoutjarWidget widgetId="YOUR_WIDGET_ID" />
}For code splitting:
import dynamic from 'next/dynamic'
const ShoutjarWidget = dynamic(
() => import('@shoutjar/react').then(mod => mod.ShoutjarWidget),
{ ssr: false, loading: () => <div>Loading testimonials...</div> }
)
export default function Testimonials() {
return <ShoutjarWidget widgetId="YOUR_WIDGET_ID" />
}Widgets work fine with Static Site Generation (SSG). They hydrate client-side after the page loads.
Shoutjar updates independently of your Next.js build. New testimonials appear without redeploying.
NEXT_PUBLIC_SHOUTJAR_WIDGET_ID=your_widget_id<ShoutjarWidget
widgetId={process.env.NEXT_PUBLIC_SHOUTJAR_WIDGET_ID!}
/>| Prop | Type | Default | Description |
|---|---|---|---|
widgetId | string | required | Your Shoutjar widget ID |
style | string | 'carousel' | Widget style: grid, carousel, marquee, badge, single |
theme | string | 'auto' | Color theme: light, dark, auto |
maxItems | number | undefined | Maximum testimonials to show |
showRating | boolean | true | Show star ratings |
showSource | boolean | true | Show source icons (Twitter, G2, etc.) |
className | string | undefined | Custom CSS class |
<ShoutjarWidget
widgetId="YOUR_WIDGET_ID"
className="border rounded-lg shadow-lg"
/>CSS variables for theming:
:root {
--shoutjar-primary: #3B82F6;
--shoutjar-background: #F9FAFB;
--shoutjar-text: #111827;
}Use lazyOnload for script tag -- don't block initial render
Dynamic import for pages where testimonials are below the fold
Single widget instance per page when possible
Environment variables -- don't hardcode widget IDs
Shoutjar widgets are designed to not impact:
Loads after main content
Non-blocking JavaScript
Fixed dimensions, no layout shift
import { ShoutjarWidget } from '@shoutjar/react'
export function Hero() {
return (
<section className="py-20">
<h1>Your Product</h1>
<p>Your value proposition</p>
<button>Get Started</button>
<ShoutjarWidget
widgetId="YOUR_WIDGET_ID"
style="badge"
className="mt-4"
/>
</section>
)
}import { ShoutjarWidget } from '@shoutjar/react'
export default function Pricing() {
return (
<main>
<PricingTable />
<section className="mt-16">
<h2>What Our Customers Say</h2>
<ShoutjarWidget
widgetId="YOUR_WIDGET_ID"
style="grid"
maxItems={6}
/>
</section>
</main>
)
}import { ShoutjarWidget } from '@shoutjar/react'
export function Footer() {
return (
<footer>
<ShoutjarWidget
widgetId="YOUR_WIDGET_ID"
style="marquee"
className="border-t"
/>
</footer>
)
}Shoutjar pulls in reviews from multiple platforms into one unified widget
Shoutjar monitors mentions of your product across the web -- even untagged tweets and Reddit threads.
All sources display in one unified widget -- no multiple integrations needed.
For advanced use cases, fetch testimonials directly via API and render them in Server Components.
export async function getTestimonials(widgetId: string) {
const res = await fetch(
`https://api.shoutjar.com/v1/widgets/${widgetId}/testimonials`,
{
headers: {
'Authorization': `Bearer ${process.env.SHOUTJAR_API_KEY}`
},
next: { revalidate: 3600 }
}
)
return res.json()
}import { getTestimonials } from '@/lib/shoutjar'
export default async function Home() {
const testimonials = await getTestimonials('YOUR_WIDGET_ID')
return (
<main>
{testimonials.map(t => (
<div key={t.id}>
<p>{t.content}</p>
<span>{t.author}</span>
</div>
))}
</main>
)
}Yes. Both script tag and React component work with Next.js 13+ App Router. Use 'use client' directive for the React component.
Yes. Works the same way -- add script in _app.tsx and use widget anywhere.
Yes. Widgets render client-side after hydration. No SSR errors. Works with SSG and ISR.
Script tag: 0KB (external). React component: ~3KB gzipped.
Yes. Use className prop, CSS variables, or configure in Shoutjar dashboard.
No. Designed for minimal impact -- lazy loading, no layout shift, non-blocking.
Yes. Each widget can have different styles and configurations.
Yes. Full type definitions included with the React package.
Script tag or React component. SSR compatible. TypeScript support. Ship in 5 minutes.
Get Started Free