Developer installation guide
How to embed Google reviews in Next.js App Router
Let Next.js load the widget script and give the reviews a stable place to appear on your page.
Updated October 2026
Quick answer
- Render
<div data-verifiedwidget-id="YOUR_WIDGET_ID" />where the reviews belong. - Load
https://verifiedwidget.com/widget.jsonce withnext/script, withoutdata-widget-id. - Deploy and check the published route, not only
next dev.
The full code for both steps is below.
Copy your saved widget's details
First connect your business in VerifiedWidget using its Google Maps link. Wait until the imported reviews and business name look right. Make a widget, choose its layout, save it, and open the dashboard's Embed dialog. The normal HTML code has this exact shape; the ID below is an example and must be replaced with your own:
<script src="https://verifiedwidget.com/widget.js" data-widget-id="c123456789012345678901234" crossorigin="anonymous" async></script>For a static HTML page, that tag works as written. In a Next.js App Router page, next/script is the built-in way to load an outside script. There is one placement detail: Next.js may put a loaded script in the document body rather than beside the component where you wrote <Script />. The VerifiedWidget loader normally places its display beside the executing script, so a direct translation of the dashboard tag can put reviews at the bottom of the page. Use the loader's supported placeholder for precise placement.
Add the page with next/script
Create or edit the App Router page that should show reviews, such as app/reviews/page.tsx. Copy the example below and replace the example ID inside data-verifiedwidget-id with your saved widget ID. The script source stays the same. This uses no extra package and can stay a server component; the browser script handles the actual review display.
// app/reviews/page.tsx
import Script from "next/script";
export default function ReviewsPage() {
return (
<main>
<h1>Customer reviews</h1>
<div
data-verifiedwidget-id="c123456789012345678901234"
style={{ minHeight: 320 }}
/>
<Script
src="https://verifiedwidget.com/widget.js"
strategy="afterInteractive"
crossOrigin="anonymous"
/>
</main>
);
}The empty div marks exactly where the reviews belong. The widget loader recognizes data-verifiedwidget-id, fetches the saved widget, and fills that element after the script runs. Notice that the Script element has no data-widget-id. That is intentional: giving the script an ID as well as the placeholder could create a second display wherever Next.js inserts the tag. Use only one placement method per widget on this page.
The Embed dialog's SEO tab also has a Next.js option. It gives a small server-side snippet that fetches your business details as JSON-LD (structured data for search engines) and prints them in the page. That part is optional and separate from the visible widget.
The markup from the server includes the heading and an empty placeholder. That makes its first HTML predictable. The browser adds reviews afterward without React trying to recreate those review cards during hydration. In plain terms, hydration is when React connects browser behavior to the HTML sent by the server. Avoid rendering a second set of cards from server data inside the same placeholder or changing its ID between server and browser. A stable placeholder avoids mismatched initial markup.
Choose when the script loads
The example uses afterInteractive. According to the installed Next.js 16 Script documentation, that is the default strategy: it loads after some of the page becomes interactive. It is a reasonable starting point when reviews are in the visible page content. You can leave the strategy prop out and get the same default behavior. If the review section is far below the first screen and can wait, lazyOnload loads during browser idle time. That can delay the moment reviews appear, so check the live page before choosing it.
beforeInteractive belongs in the root layout for scripts needed throughout the whole site. A review widget on one page usually does not need that earlier, site-wide load. The worker strategy is experimental and the installed Next.js documentation says it does not work with the App Router. Keep the example on the page or in the narrowest layout that should display the widget. A script in the root layout can run on routes without reviews and complicate placement.
Next.js loads a given Script source once as visitors move between routes in the same app. VerifiedWidget's loader watches for new placeholders in single-page navigation, so the script does not need to be fetched again merely because a visitor opened the reviews route later. Test direct entry to the reviews URL and navigation to it from another page. Both paths should show the same display. If you need a different saved widget on another route, use that route's own placeholder ID.
Badge and review pop-up layouts float over the page, so the loader moves them to the end of the page body. They stay on screen after a client-side route change, and a return visit adds a second copy. Put the placeholder for a floating layout in app/layout.tsx, so it renders once for the whole site.
Make room and check the layout
The example reserves 320 pixels before reviews arrive. This reduces the visible jump when the widget adds cards to the page. It is only a starting value. A wide review grid may grow taller on a phone; a floating badge needs none, because it sits in a screen corner. Check your chosen widget in its mobile preview, then adjust the placeholder's minHeight on the published site. Avoid height with overflow:hidden unless clipping is intended.
Put the placeholder in a section with enough width for the selected layout. A narrow parent column or overflow rule can cut off a carousel even when the script loads correctly. Inspect the surrounding layout if cards seem missing. The widget's internal styles are isolated, but your page still controls the available width, spacing, and whether the surrounding area is visible.
Allow the script in your Content Security Policy
If your Next.js deployment sends a Content Security Policy (CSP), the browser may block the widget even though the code is correct. Allow https://verifiedwidget.com in script-src to load widget.js and in connect-src so it can fetch review data. The following directives illustrate the required origins. Merge them into your existing policy; do not replace a complete policy with these three lines.
// Illustrative CSP directives; merge with your existing policy.
script-src 'self' https://verifiedwidget.com;
connect-src 'self' https://verifiedwidget.com;
img-src 'self' data: https://verifiedwidget.com https://*.googleusercontent.com;Review avatars, media, and optional theme fonts can come from other hosts. If your policy restricts images, media, fonts, or styles, open the browser console. It shows which source was refused. Then allow only the sources your chosen widget actually needs. A strict policy built around a fresh nonce for each request needs its own script trust setup. Next.js's current CSP documentation explains that nonce support depends on dynamic rendering. Ask the maintainer of that policy to review the change; a simple host allowlist may not apply when strict-dynamic is in use.
Troubleshoot the published route
If the area stays blank, verify the exact widget ID and that the page includes the placeholder in the HTML. In the browser network panel, check whether widget.js loads, then whether the /api/embed/ data request succeeds. A CSP error tells you which policy directive blocked it. A 403 can mean your production or preview domain is missing from the widget's allowed-domain list. A 404 can mean the widget ID is wrong or the widget was deleted. A successful request with no cards may mean no imported reviews meet the widget's filters.
If reviews appear at the bottom of the document as well as in the intended section, search for another plain script tag. Look for one with data-widget-id in your page, layout, or tag manager. Keep the placeholder and one source-only Script for this method. If reviews appear only on a hard refresh, confirm the script was not removed on client navigation. Also confirm the placeholder has the same ID on the new route. Test without a script-blocking extension and on a phone-sized screen.
A review widget can show visitors real customer feedback, but adding it does not qualify a business's own reviews for Google star results. Read our review-star guide before adding search markup. For a non-Next React app, see the React component guide.
Frequently asked questions
Can this stay a server component?
Yes. The page renders a stable empty placeholder, and the browser-loaded widget script fills it after the page becomes interactive.
Why is data-widget-id missing from the Script element?
The placeholder carries the widget ID and controls placement. Adding the ID to the Script element too can create another widget near the script tag.
Should I use beforeInteractive for reviews?
Usually no. It belongs in the root layout for scripts needed across the site. afterInteractive is the normal starting point for a page-level review display.
Will it work after Next.js route changes?
Yes. The loader watches for new placeholders, while Next.js keeps the script source loaded across client-side navigation. Test both direct and in-app visits.
Why does the widget show a blank area at first?
The reserved height reduces layout shift while the script and review data load. Adjust it to suit your selected layout and screen size.
Put reviews on your Next.js page
Build one free widget, copy its ID, and place the script and placeholder on the route visitors use.