Provides helper functions that generate JSON-LD objects from Payload product documents to enable Google rich snippets. You render the output in script tags within Next.js pages and layouts; an optional plugin entry in the Payload config sets the currency. It expects prices stored in cents and products fetched with enough depth to populate nested categories.
Schema.org JSON-LD utilities for Payload CMS v3 e-commerce — Product, BreadcrumbList, ItemList, WebSite, Organization. Enables Google rich snippets with zero config.
Installation
npm i payload-plugin-seo-jsonld1
Stars
0
Forks
0
Open Issues
1 weeks ago
Last Updated
0
Weekly Downloads
v1.0.2
npm Version
21.6 KB
Unpacked Size
0
Dependencies
Trends appear after 7 days of data. Daily snapshots have not started yet.
Trends appear after 7 days of data. Daily snapshots have not started yet.
Each point is npm's rolling 7-day download count.
Health Score
Composite score based on GitHub activity, stars, npm downloads, publish recency, dependency count, and package size.
Badge for your README
[](https://payloaddirectory.dev/plugins/1270763175-root)<a href="https://payloaddirectory.dev/plugins/1270763175-root"><img src="https://payloaddirectory.dev/badge/1270763175-root.svg" alt="Payload Directory"></a>README
Schema.org JSON-LD utilities for Payload CMS v3 e-commerce. Enables Google rich snippets (product cards, carousels, breadcrumbs) with battle-tested helpers built on real production deployments across 23 European shops.
⭐ If this plugin saves you time, star it on GitHub. It is the main way other Payload developers find it.
AggregateOffer with lowPrice, stock status, variants supportnpm install payload-plugin-seo-jsonld
// payload.config.ts
import { seoJsonLdPlugin } from 'payload-plugin-seo-jsonld'
export default buildConfig({
plugins: [
seoJsonLdPlugin({ currency: 'EUR' }),
],
})
// src/app/(app)/products/[slug]/page.tsx
import { buildProductJsonLd, buildBreadcrumbJsonLd } from 'payload-plugin-seo-jsonld'
export default async function ProductPage({ params }) {
const product = await fetchProduct(params.slug)
const siteUrl = process.env.NEXT_PUBLIC_SERVER_URL!
const productJsonLd = buildProductJsonLd({
product,
siteUrl,
currency: 'EUR',
// Optional: plug in review data
averageRating: 4.8,
reviewCount: 42,
})
const breadcrumbJsonLd = buildBreadcrumbJsonLd({ product, siteUrl })
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(productJsonLd) }}
/>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }}
/>
{/* your page content */}
</>
)
}
// src/app/(app)/shop/page.tsx
import { buildItemListJsonLd } from 'payload-plugin-seo-jsonld'
export default async function ShopPage() {
const products = await fetchProducts()
const siteUrl = process.env.NEXT_PUBLIC_SERVER_URL!
// ⚠️ Only one ItemList per page — Google invalidates duplicates
const itemListJsonLd = buildItemListJsonLd({
products: products.docs,
siteUrl,
currency: 'EUR',
listName: 'Our Products',
})
return (
<>
{itemListJsonLd && (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(itemListJsonLd) }}
/>
)}
{/* your page content */}
</>
)
}
// src/app/layout.tsx
import { buildWebSiteJsonLd, buildOrganizationJsonLd } from 'payload-plugin-seo-jsonld'
export default function RootLayout({ children }) {
const siteUrl = process.env.NEXT_PUBLIC_SERVER_URL!
const websiteJsonLd = buildWebSiteJsonLd({
siteUrl,
siteName: 'My Shop',
description: 'Premium jewelry across Europe',
})
const orgJsonLd = buildOrganizationJsonLd({
siteUrl,
name: 'My Shop',
logo: `${siteUrl}/logo.png`,
})
return (
<html>
<body>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(websiteJsonLd) }}
/>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(orgJsonLd) }}
/>
{children}
</body>
</html>
)
}
buildProductJsonLd(options)| Option | Type | Default | Description |
|---|---|---|---|
product | object | required | Payload product document |
siteUrl | string | required | Base URL (e.g. https://myshop.com) |
currency | string | 'EUR' | ISO 4217 currency code |
averageRating | number | — | Average review rating |
reviewCount | number | — | Number of reviews |
buildBreadcrumbJsonLd(options)| Option | Type | Description |
|---|---|---|
product | object | Payload product with populated categories |
siteUrl | string | Base URL |
Supports nested categories:
Shop → Parent Category → Sub Category → Product
buildItemListJsonLd(options)| Option | Type | Default | Description |
|---|---|---|---|
products | array | required | Array of Payload product docs |
siteUrl | string | required | Base URL |
currency | string | 'EUR' | ISO 4217 currency code |
listName | string | 'Products' | Name of the list |
Returns
nullif products array is empty — safe to use with conditional rendering.
buildWebSiteJsonLd(options)| Option | Type | Description |
|---|---|---|
siteUrl | string | Base URL |
siteName | string | Site name |
description | string | Optional description |
buildOrganizationJsonLd(options)| Option | Type | Description |
|---|---|---|
siteUrl | string | Base URL |
name | string | Organization name |
logo | string | Logo URL |
description | string | Optional description |
3990 = €39.90). This plugin automatically divides by 100.ItemList scripts. Always check before adding.lowPrice not price — Google requires lowPrice for AggregateOffer validation. This plugin handles it correctly.depth: 3 to populate categories and their parents.# Check lowPrice on product page
curl -s "https://myshop.com/products/my-product" | grep "lowPrice"
# Check single ItemList on shop page
curl -s "https://myshop.com/shop" | grep -o '"@type":"ItemList"' | wc -l
# → must return 1
Test with Google Rich Results Test.
I'm Camille Guerineau. I spent ten years as a jewelry maker and founder of Loch Ness Paris before going back to full-stack development. I build Payload CMS shops for real e-commerce, and I publish the pieces that were missing.
payload-plugin-reviews, customer reviews with moderation and star ratingsverumano)Need help with a Payload CMS v3 shop, SEO or rich snippets? Get in touch through lochness-paris.com.
MIT © Camille Guerineau
sitemap
Chunked, lazily cached XML sitemaps with hook-driven invalidation and robots.txt helpers
6123/wk15v3
payload seo analyzer
Offline on-page SEO analysis, admin dashboard, sitemaps and JSON-LD for Payload 3 and Next.js
600/wk16v3
advanced seo plugin
Per-document SEO meta group, global defaults, hreflang alternates, OG images and JSON-LD builders for Payload
220/wk3v3
seo
Manage SEO metadata for Payload collections and globals from within the admin panel
100169.7k/wk45.4kv3
broken link checker
Scan Payload collections for broken links and store the results in a reporting collection
79411/wk114v3
payload plugin sitemap
Generate sitemap.xml files from Payload content with a background job and store them in any storage adapter
640/wk5v4