A Next.js App Router app that renders the Cloudinary Product Gallery Widget v2 on the server and hydrates it on the client, using the @cloudinary/product-gallery npm package.
It demonstrates the main v2 features:
- Server-side rendering:
resolveConfig()runs in a Server Component, and theGallerycomponent renders the resolved config in a Client Component. The gallery markup is in the initial HTML. viewerWidthHint: tells the widget how wide its container will be, because there's no browser on the server to measure it.- CSS-driven sizing: the widget fills its container and keeps the configured
aspectRatio. - Native responsive images: the server-rendered
<img>tags includesrcsetandsizes. - Mixed media: an image set, a video, and a 360 spin set, all resolved from tags on the server.
- CSS customization: stable class names under a
.pgwroot class, with configuration parameters for most styling and CSS overrides for the rest.
git clone https://github.com/cloudinary-devs/cloudinary-product-gallery-nextjs.git
cd cloudinary-product-gallery-nextjs
npm install
npm run devOpen http://localhost:3000, and view the page source to see the gallery in the server-rendered HTML.
To use your own assets, change cloudName and mediaAssets in app/page.tsx. Make sure client-side asset lists are enabled for your product environment, which is required for tag-based mediaAssets.
| File | Purpose |
|---|---|
app/layout.tsx |
Imports the widget's stylesheet (@cloudinary/product-gallery/index.css) once. |
app/page.tsx |
Server Component. Calls resolveConfig() from @cloudinary/product-gallery/server, with viewerWidthHint matching the container's max-width. |
app/GalleryClient.tsx |
Client Component. Renders <Gallery resolvedConfig={...} />. |
app/globals.css |
CSS overrides for what the configuration doesn't cover. |
- The npm package is ESM only and requires Node 18 or later, plus
reactandreact-dom18 or 19. viewportBreakpointsoverrides apply after hydration, so the server markup always matches the first client render.- The
Gallerycomponent doesn't expose the event API or the imperative instance methods (on,setItem, and so on). If you need them, use the script embed instead.