spiritracking-arch

payload plugin seo jsonld

by spiritracking-arch
SEO & metadata
v3
^3.0.0
MIT

Overview

Summary generated from the README

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.

Features

  • Product JSON-LD using AggregateOffer with lowPrice and stock status
  • Optional AggregateRating from your own review data
  • BreadcrumbList built from nested category hierarchies
  • ItemList for shop and category pages, returning null for empty lists
  • WebSite with SearchAction and Organization structured data
  • Automatic conversion of prices from cents to the chosen currency

Use cases

  • Add Google product rich snippets to a Payload and Next.js online shop
  • Generate breadcrumb structured data for nested product categories
  • Publish site-wide WebSite and Organization markup from the root layout

From the author

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-jsonld

1

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

GitHub stars

Trends appear after 7 days of data. Daily snapshots have not started yet.

npm weekly downloads

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

65/100Good

Composite score based on GitHub activity, stars, npm downloads, publish recency, dependency count, and package size.

Badge for your README

Payload Directory health badge
Markdown
[![Payload Directory](https://payloaddirectory.dev/badge/1270763175-root.svg)](https://payloaddirectory.dev/plugins/1270763175-root)
HTML
<a href="https://payloaddirectory.dev/plugins/1270763175-root"><img src="https://payloaddirectory.dev/badge/1270763175-root.svg" alt="Payload Directory"></a>
Updated Oct 11, 2026Created Jun 16, 2026Published Oct 11, 2026

README

payload-plugin-seo-jsonld

npm version npm downloads GitHub stars Payload v3 License: MIT

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.

Features

  • 🛍️ Product — AggregateOffer with lowPrice, stock status, variants support
  • ⭐ AggregateRating — plug in your review data for star ratings in Google
  • 🧭 BreadcrumbList — nested category hierarchy (parent → child → product)
  • 📋 ItemList — shopping carousel for shop/category pages
  • 🌐 WebSite — SearchAction for Google Sitelinks Searchbox
  • 🏢 Organization — brand identity in search results
  • 🔒 Anti-duplicate protection for ItemList (Google invalidates 2+ per page)
  • 💶 Automatic price conversion from cents to euros (or any currency)

Installation

npm install payload-plugin-seo-jsonld

Usage

1. Add to your Payload config (optional)

// payload.config.ts
import { seoJsonLdPlugin } from 'payload-plugin-seo-jsonld'

export default buildConfig({
  plugins: [
    seoJsonLdPlugin({ currency: 'EUR' }),
  ],
})

2. Product page — Product + BreadcrumbList

// 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 */}
    </>
  )
}

4. Root layout — WebSite + Organization

// 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>
  )
}

API Reference

buildProductJsonLd(options)

OptionTypeDefaultDescription
productobjectrequiredPayload product document
siteUrlstringrequiredBase URL (e.g. https://myshop.com)
currencystring'EUR'ISO 4217 currency code
averageRatingnumber—Average review rating
reviewCountnumber—Number of reviews

buildBreadcrumbJsonLd(options)

OptionTypeDescription
productobjectPayload product with populated categories
siteUrlstringBase URL

Supports nested categories: Shop → Parent Category → Sub Category → Product

buildItemListJsonLd(options)

OptionTypeDefaultDescription
productsarrayrequiredArray of Payload product docs
siteUrlstringrequiredBase URL
currencystring'EUR'ISO 4217 currency code
listNamestring'Products'Name of the list

Returns null if products array is empty — safe to use with conditional rendering.

buildWebSiteJsonLd(options)

OptionTypeDescription
siteUrlstringBase URL
siteNamestringSite name
descriptionstringOptional description

buildOrganizationJsonLd(options)

OptionTypeDescription
siteUrlstringBase URL
namestringOrganization name
logostringLogo URL
descriptionstringOptional description

Important Notes

  • Prices in cents — Payload stores prices in cents (3990 = €39.90). This plugin automatically divides by 100.
  • ItemList deduplication — Google invalidates pages with 2+ ItemList scripts. Always check before adding.
  • lowPrice not price — Google requires lowPrice for AggregateOffer validation. This plugin handles it correctly.
  • Depth 3 required — Make sure to fetch products with depth: 3 to populate categories and their parents.

Verification

# 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.

About the author

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.

Need help with a Payload CMS v3 shop, SEO or rich snippets? Get in touch through lochness-paris.com.

License

MIT © Camille Guerineau

Similar plugins