Next.js and Vercel Shop
@meisei/widgets/next renders the React widgets on the
server. Reviews, ratings and their structured data are in the HTML your shopper's browser
receives, and the browser makes no request for the first view.
This page covers the App Router, then the Pages Router, then what to change in Vercel Shop and in Next Commerce, the template it replaced.
Install
The package works with React 19 and the App Router. It does not import anything from
next, so it also runs under the Pages Router, Remix or any other server.
Set your keys
Make both keys on
Settings → API keys, then put them
in .env.local:
NEXT_PUBLIC_SITE_URL is your storefront's own address. The structured data on a product
page names the page by URL, and a server component cannot see the address it is answering,
so this is where it comes from. On Vercel you can leave it out, and the widgets use the
production host Vercel sets instead.
The secret key stays on the server, and the server rendering uses it. It counts against your store's API allowance rather than the publishable key's per visitor limit, which server rendering for every shopper would use up in a minute. The publishable key is the one that reaches the browser, for the things a shopper does after the page loads: sorting, voting, writing a review. See Calling the widget API from a server and Rate limits.
MEISEI_API_URL is your store's own address with /api on the end. Copy it from
Settings → General, under Address.
There is no default, because your store's data is served from your store's own address. If
this is missing, the widgets say what to set and stop. You can also pass it as the provider's baseUrl prop.
The widget endpoints need no particular permission on the secret key. Signing a shopper in
does: give the key customers:write if you pass customerEmail below.
Add the provider
One provider in app/layout.tsx covers the whole app. It reads the environment variables
above, so it usually takes no props at all.
It also links the stylesheet your appearance settings produce, so the widgets match your
store without a build step. Pass theme={false} if you would rather style them yourself.
Pass agentTools to offer the widgets' tools to an AI agent in the shopper's browser. See
AI agents on your storefront.
Put reviews on a product page
Reviews, Rating, ReviewsCarousel, MediaGrid, Loyalty and LoyaltyPoints are
server components: each one loads its own data, then hands it to the widget. They take the
same props as the connected widgets in React widgets, plus
revalidate and cache. Reviews passes context and pageContext through to the
instant answer box. On the server there is no page to read, so pass pageContext yourself
if the product copy is not in the page's structured data.
The page URL in the structured data is your site URL plus /products/{slug}. If your
product pages live somewhere else, tell the provider once with
productPath="/product/{slug}", or pass productUrl on a widget for one page. A
relative productUrl is resolved against your site URL.
The widgets that only run in the shopper's browser are re-exported from the same place,
because there is nothing for a server to render first: AddToWishlistWidget,
CustomerWishlistsWidget, WishlistsModalWidget, WishlistBannerWidget and
LoyaltyLauncherWidget.
What the server renders
- The reviews, the summary and the rating are in the HTML, so a shopper on a slow connection reads them before the JavaScript arrives and nothing shifts when it does.
- The
Productstructured data is in the HTML too, which is what a search engine that does not run JavaScript reads, with the page URL built as described above. - Every server widget on a page asks Meisei for its data in the same request, so a page with reviews, ratings and the loyalty panel is one call to Meisei, not one per widget.
- The browser makes no request for that first view. Sorting, filtering, Load more, voting and writing a review still go straight to Meisei from the browser.
Signed-in shoppers
Give the provider the email address of whoever is signed in. Meisei signs it on the server and hands the browser only the signature, never the key that made it.
That is what puts a shopper's own wishlists, points and rewards on the page, what fills
their email address in when they write a review, and what lets a review be matched to their
orders for the verified buyer badge. <Reviews> loads with it too, so a store that only
accepts verified buyers renders the right form, or the note in its place, on the server.
Leave it out for a guest.
Caching and revalidation
Server rendering asks Meisei for the data on every request unless you say otherwise:
revalidate and cache go straight to fetch, so everything Next.js does with them
applies. Sixty seconds is a reasonable starting point for a product page, because reviews do
not change often. A shopper who has just written one sees it right away either way, because
that request comes from their browser.
The Pages Router
There are no server components under the Pages Router, so load the data in
getServerSideProps with the loader for the widget, and pass it to the widget as
initialData:
loadProductReviews, loadProductRating, loadReviewsCarousel, loadMediaGrid,
loadLoyalty and loadLoyaltyEarnable are plain async functions with no framework in
them. loadProductRating takes a list of slugs, so a category page loads every rating on
it in one request. See React widgets for the
whole list.
Vercel Shop
Vercel Shop is Vercel's Shopify storefront template,
the successor to Next Commerce. Its product pages live at /products/[handle], which is
where the widgets look by default, and its product handle is the slug Meisei stores, so
there is nothing to map. Against the repository as it stands on 17 October 2026 (Next.js
16.4, React 19.3):
- Install and set your keys.
pnpm add @meisei/widgets, then the variables from Set your keys in.env.localand in your Vercel project's environment variables. Off Vercel, setNEXT_PUBLIC_SITE_URLto the same origin as the template'ssite.url. app/layout.tsx: wrap everything inside<CartProviderWrapper>in<MeiseiNextProvider>. Add<LoyaltyLauncherWidget>after<Footer />if you want the Rewards button on every page.app/products/[handle]/page.tsx: inProductPageContent, add<Reviews productSlug={product.handle} />after<ProductDetailSection>. It is already inside the page's<Suspense>, which Cache Components asks of anything loaded per request.components/product-detail/product-detail-section.tsx: add<Rating productSlug={handle} />under the<h1>inProductInfoArea, and<AddToWishlistWidget productSlug={handle} />beside the buy buttons if you use wishlists.- Keep one
Productblock.ProductDetailSectionrenders the template'sProductSchema. PassstructuredData={false}toReviewsto keep it, or remove<ProductSchema>and keep Meisei's, which carries the rating and the reviews.
Vercel Shop turns on Cache Components, so the page's static shell is served first and anything loaded per request streams in after it. The widgets are part of what streams, so the reviews are fresh on every visit and each product page view is one request to Meisei against your store's API allowance.
If you turn on Shopify customer accounts (auth.isEnabled), pass the signed-in shopper's
email from getCustomerProfile() as customerEmail, as in
Signed-in shoppers. Reading the session is per request, so it
belongs inside a <Suspense> boundary, not in the root layout's static shell.
Connect the same store in Settings → Shopify so review requests go out after each order. See Connecting Shopify.
Next Commerce
Next Commerce is Vercel's Shopify storefront. Its
product handle is the same slug Meisei stores, so product.handle is what every widget
takes. Three files, against the repository as it stands on 9 September 2026 (Next.js 15.6,
React 19):
app/layout.tsx: wrap the existing<main>in<MeiseiNextProvider productPath="/product/{slug}">. Next Commerce's product pages are singular, so the default path would name a page that is not there. Its site URL is Vercel's production host, so there is nothing else to set.app/product/[handle]/page.tsx: add<Reviews productSlug={product.handle} />below the gallery and description. The page already emits its own product structured data, so passstructuredData={false}to keep oneProductblock on the page, or drop the template's and keep Meisei's, which carries the rating and the reviews.components/product/product-description.tsx: add<Rating productSlug={product.handle} />under the title, beside the price.
Connect the same store in Settings → Shopify so the products, orders and customers behind those reviews stay in step. See Connecting Shopify.
If something isn't working
- "Server rendering with the publishable key" in your server log.
MEISEI_SECRET_KEYis not set, so the rendering is using the browser key and will be rate limited by visitor. Set it and restart. - The widgets are empty and your server log has a Meisei error. A loader that fails never fails the page. The widget falls back to loading in the browser. The log line says which request it was.
- "only works in a Client Component". Import the widget from
@meisei/widgets/next, or mark your own file"use client"if you are composing a widget from its parts. - No styling. Check that the theme stylesheet loads, or import
@meisei/widgets/theme.cssyourself if you turned it off.
Next steps
- React widgets for the props, the parts and composing a widget.
- Storefront widgets for appearance and the class names.
- Connecting Shopify to keep the store data in step.