JSON-LD Product Schema: Complete Implementatie Gids per Platform (2026)

JSON-LD structured data is de directe lijn tussen jouw productpagina en ChatGPT, Perplexity én Google Rich Results. Zonder het zijn je producten een raadsel voor AI. Met de juiste implementatie word je geciteerd, gevonden en gekocht. In deze gids: werkende code voor elk groot e-commerce platform.

In dit artikel

  1. Waarom JSON-LD cruciaal is voor GEO
  2. De anatomie van een perfect Product schema
  3. Implementatie per platform
  4. 7 veelgemaakte fouten (en hoe je ze vermijdt)
  5. Hoe je je markup test en valideert
  6. Veelgestelde vragen

Waarom JSON-LD cruciaal is voor GEO

AI-zoekmachines zoals ChatGPT, Perplexity en Google AI Overviews beantwoorden vragen door webpagina's te begrijpen — niet alleen te indexeren. Ze "lezen" je productpagina en proberen te ontdekken: wat kost dit product? Wat zijn de reviews? Is het op voorraad? Welk merk maakt het?

Als die informatie verborgen zit in HTML-tabellen, JavaScript-laadlogica of gewoon niet aanwezig is, geeft de AI een vaag antwoord of — erger — verwijst door naar een concurrent die zijn data wél netjes aanbiedt.

JSON-LD lost dit op. Het is een klein stukje code in de <head> van je pagina dat machine-leesbaar vertelt: "Dit is een product, het heet X, kost Y euro, heeft 4,8 sterren van 247 reviews, en is op voorraad." Voor AI is dat het verschil tussen een vage hint en een directe bevestiging.

In onze analyse van 500+ Nederlandse webshops had slechts 23% volledige Product JSON-LD implementatie. Van die 23% ontbrak bij 61% het GTIN/EAN-nummer — precies het veld dat AI gebruikt om producten te matchen met andere databronnen.

De impact op je GEO-score is direct: de dimensie "Structured Data" weegt voor 30% mee in onze scoring. Een webshop zonder enige JSON-LD scoort hier standaard een 0. Met een volledige implementatie spring je direct naar 80–100 punten op dit onderdeel.

De anatomie van een perfect Product schema

Schema.org definieert tientallen velden voor een Product. Niet alle velden zijn even waardevol voor GEO. Hieronder de prioriteitstabel — van essentieel tot nice-to-have:

Veld Prioriteit GEO Impact Google Rich Results
name Verplicht Hoog Vereist
description Verplicht Hoog Aanbevolen
image Verplicht Middel Vereist
offers.price Verplicht Hoog Vereist voor prijs-resultaten
offers.availability Belangrijk Hoog Vereist
brand Belangrijk Hoog Aanbevolen
aggregateRating Belangrijk Hoog Vereist voor sterren in SERP
sku Aanbevolen Middel Aanbevolen
gtin / gtin13 (EAN) Aanbevolen Hoog voor AI Aanbevolen
category Aanbevolen Middel Aanbevolen
review (individueel) Bonus Hoog voor citaties Bonus

Het GTIN/EAN-nummer is bijzonder waardevol voor AI. ChatGPT en Perplexity gebruiken het om je product te koppelen aan externe databronnen: vergelijkingssites, productdatabases, andere shops. Dat vergroot de kans op vermeldingen enorm.

Het complete template

Dit is het ideale JSON-LD Product schema. Kopieer dit als startpunt:

JSON-LD
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Productnaam hier",
  "description": "Uitgebreide productomschrijving. Minimaal 150 woorden voor maximale GEO-score.",
  "image": [
    "https://example.nl/images/product-vooraanzicht.jpg",
    "https://example.nl/images/product-zijaanzicht.jpg"
  ],
  "sku": "ARTIKEL-001",
  "gtin13": "1234567890123",
  "brand": {
    "@type": "Brand",
    "name": "Merknaam"
  },
  "category": "Categorie > Subcategorie",
  "offers": {
    "@type": "Offer",
    "url": "https://example.nl/product/productnaam",
    "priceCurrency": "EUR",
    "price": "49.95",
    "priceValidUntil": "2026-12-31",
    "availability": "https://schema.org/InStock",
    "itemCondition": "https://schema.org/NewCondition",
    "seller": {
      "@type": "Organization",
      "name": "Naam van je winkel"
    }
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.7",
    "reviewCount": "128",
    "bestRating": "5",
    "worstRating": "1"
  }
}
</script>

Implementatie per platform

Hieronder de concrete implementatiemethode per platform — inclusief code snippets die je direct kunt gebruiken.

WooCommerce: PHP-snippet in functions.php

WooCommerce heeft ingebouwde schema markup via Yoast SEO of RankMath, maar beide plugins missen often het gtin13 veld en genereren geen reviewdata. De meest flexibele oplossing: voeg een custom PHP-snippet toe aan je child theme's functions.php.

Yoast SEO genereert ook Product schema. Als je Yoast gebruikt, voeg dit snippet dan NIET toe — anders krijg je dubbele markup wat Google straft. Gebruik het snippet alleen als je Yoast/RankMath uitschakelt, of pas de Yoast-output aan via hun filters.
PHP — functions.php
/**
 * GEO-geoptimaliseerd JSON-LD Product schema voor WooCommerce
 * Toevoegen aan: child-theme/functions.php
 */
function geo_product_jsonld() {
    if ( ! is_product() ) return;
    global $product;

    $product = wc_get_product();
    if ( ! $product ) return;

    $rating   = $product->get_average_rating();
    $reviews  = $product->get_review_count();
    $gtin     = $product->get_meta('_gtin'); // Vereist ACF of custom meta

    $schema = [
        '@context' => 'https://schema.org',
        '@type'    => 'Product',
        'name'     => $product->get_name(),
        'description' => wp_strip_all_tags( $product->get_description() ),
        'image'    => wp_get_attachment_url( $product->get_image_id() ),
        'sku'      => $product->get_sku(),
        'brand'    => [
            '@type' => 'Brand',
            'name'  => $product->get_attribute('pa_merk') ?? get_bloginfo('name'),
        ],
        'offers'   => [
            '@type'          => 'Offer',
            'url'            => get_permalink(),
            'priceCurrency'  => get_woocommerce_currency(),
            'price'          => $product->get_price(),
            'availability'   => $product->is_in_stock()
                ? 'https://schema.org/InStock'
                : 'https://schema.org/OutOfStock',
            'itemCondition'  => 'https://schema.org/NewCondition',
            'seller'         => [
                '@type' => 'Organization',
                'name'  => get_bloginfo('name'),
            ],
        ],
    ];

    // EAN/GTIN toevoegen als aanwezig
    if ( $gtin ) {
        $schema['gtin13'] = $gtin;
    }

    // Reviews toevoegen als aanwezig
    if ( $rating && $reviews > 0 ) {
        $schema['aggregateRating'] = [
            '@type'       => 'AggregateRating',
            'ratingValue' => number_format( (float)$rating, 1 ),
            'reviewCount' => (int)$reviews,
            'bestRating'  => '5',
            'worstRating' => '1',
        ];
    }

    echo '<script type="application/ld+json">'
        . wp_json_encode( $schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES )
        . '</script>';
}
add_action( 'wp_head', 'geo_product_jsonld' );
Tip EAN opslaan: Maak een custom product meta veld _gtin via ACF (Advanced Custom Fields) of het gratis WooCommerce plugin "GTIN, UPC, EAN for WooCommerce". Dit veld wordt dan automatisch meegenomen in de JSON-LD output.

Shopify: Liquid template aanpassen

Shopify genereert automatisch Product schema via de product.json Liquid template. De standaard output mist echter reviews, GTIN en adequate beschrijving. Pas je product.json.liquid (of de schema sectie in je main-product.liquid) aan.

In Shopify admin: Online Store → Themes → Edit code → Zoek op application/ld+json in je thema-bestanden. In de meeste moderne themes zit dit in sections/main-product.liquid of templates/product.json.
Liquid — sections/main-product.liquid
{% comment %} GEO-geoptimaliseerd Product JSON-LD voor Shopify {% endcomment %}
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": {{ product.title | json }},
  "description": {{ product.description | strip_html | truncate: 500 | json }},
  "image": [
    {% for image in product.images limit: 3 %}
      "https:{{ image.src | img_url: 'master' }}"{% unless forloop.last %},{% endunless %}
    {% endfor %}
  ],
  "sku": {{ product.selected_or_first_available_variant.sku | json }},
  {% if product.metafields.custom.gtin != blank %}
  "gtin13": {{ product.metafields.custom.gtin | json }},
  {% endif %}
  "brand": {
    "@type": "Brand",
    "name": {{ product.vendor | json }}
  },
  "category": {{ product.type | json }},
  "offers": {
    "@type": "Offer",
    "url": "{{ shop.url }}{{ product.url }}",
    "priceCurrency": {{ shop.currency | json }},
    "price": "{{ product.price | divided_by: 100.0 }}",
    "priceValidUntil": "{{ 'now' | date: '%s' | plus: 7776000 | date: '%Y-%m-%d' }}",
    "availability": {% if product.available %}
      "https://schema.org/InStock"
    {% else %}
      "https://schema.org/OutOfStock"
    {% endif %},
    "itemCondition": "https://schema.org/NewCondition",
    "seller": {
      "@type": "Organization",
      "name": {{ shop.name | json }}
    }
  }
  {% if product.metafields.reviews.rating != blank %}
  ,"aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "{{ product.metafields.reviews.rating.value }}",
    "reviewCount": "{{ product.metafields.reviews.rating_count }}",
    "bestRating": "5",
    "worstRating": "1"
  }
  {% endif %}
}
</script>
EAN via Metafields: Ga naar Settings → Custom Data → Products → voeg een metafield toe met namespace custom en key gtin. Vul het daarna in voor elk product via het product-beheerscherm.

Magento 2: PHTML template override

Magento 2 heeft geen ingebouwde JSON-LD Product schema output. De aanbevolen aanpak: maak een custom module of voeg een PHTML-bestand toe via je theme. Hieronder de minimale implementatie via een theme template override.

Maak het bestand aan op: app/design/frontend/[Vendor]/[Theme]/Magento_Catalog/templates/product/geo-jsonld.phtml

PHP — geo-jsonld.phtml
<?php
/**
 * GEO Tool — JSON-LD Product Schema voor Magento 2
 * Template: Magento_Catalog/templates/product/geo-jsonld.phtml
 */
/** @var \Magento\Catalog\Block\Product\View $block */
$product = $block->getProduct();
$helper  = $block->helper(\Magento\Catalog\Helper\Data::class);

$imageHelper = $this->helper(\Magento\Catalog\Helper\Image::class);
$imageUrl    = $imageHelper->init($product, 'product_page_image_large')->getUrl();

$price       = $product->getFinalPrice();
$inStock     = $product->isAvailable();

// EAN opgeslagen in custom attribute 'ean'
$ean         = $product->getData('ean');

$schema = [
    '@context'    => 'https://schema.org',
    '@type'       => 'Product',
    'name'        => $product->getName(),
    'description' => strip_tags($product->getDescription() ?: $product->getShortDescription()),
    'image'       => $imageUrl,
    'sku'         => $product->getSku(),
    'brand'       => [
        '@type' => 'Brand',
        'name'  => $product->getAttributeText('manufacturer') ?: '',
    ],
    'offers'      => [
        '@type'          => 'Offer',
        'url'            => $product->getProductUrl(),
        'priceCurrency'  => 'EUR',
        'price'          => number_format($price, 2, '.', ''),
        'availability'   => $inStock
            ? 'https://schema.org/InStock'
            : 'https://schema.org/OutOfStock',
        'itemCondition'  => 'https://schema.org/NewCondition',
    ],
];

if ($ean) {
    $schema['gtin13'] = $ean;
}

// Reviews via Magento rating
$ratingSummary = $product->getRatingSummary();
if ($ratingSummary && $ratingSummary->getReviewsCount() > 0) {
    $ratingValue = round(($ratingSummary->getRatingSummary() / 100) * 5, 1);
    $schema['aggregateRating'] = [
        '@type'       => 'AggregateRating',
        'ratingValue' => (string)$ratingValue,
        'reviewCount' => (string)$ratingSummary->getReviewsCount(),
        'bestRating'  => '5',
        'worstRating' => '1',
    ];
}
?>
<script type="application/ld+json">
<?= json_encode($schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) ?>
</script>

Registreer de template via een layout XML update in je theme:

XML — catalog_product_view.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <head>
        <block class="Magento\Catalog\Block\Product\View"
               name="product.geo.jsonld"
               template="Magento_Catalog::product/geo-jsonld.phtml"
               after="-"/>
    </head>
</page>

Shopware 6: Twig template extension

Shopware 6 genereert standaard minimale schema markup. De meest schone aanpak voor Shopware is een Twig block extension in je custom thema of plugin.

Voeg dit toe aan je thema in: Resources/views/storefront/page/product-detail/index.html.twig

Twig — product-detail/index.html.twig
{% sw_extends '@Storefront/storefront/page/product-detail/index.html.twig' %}

{% block page_product_detail_schema_org %}
    {{ parent() }}

    {%- set product = page.product -%}
    {%- set currency = context.currency -%}
    {%- set baseUrl = app.request.schemeAndHttpHost -%}

    {%- set price = product.calculatedPrice.unitPrice -%}
    {%- set inStock = (product.availableStock > 0) -%}
    {%- set ean = product.ean -%}
    {%- set manufacturerName = product.manufacturer ? product.manufacturer.name : '' -%}
    {%- set reviewAvg = product.ratingAverage -%}
    {%- set reviewCount = product.reviewCount -%}

    {%- set imageUrl = '' -%}
    {%- if product.cover.media -%}
        {%- set imageUrl = baseUrl ~ '/thumbnail/' ~ product.cover.media.id ~ '/' ~ product.cover.media.fileName -%}
    {%- endif -%}

    
{% endblock %}
EAN in Shopware: Shopware 6 heeft een ingebouwd ean veld op producten. Vul dit in via het productbeheer (Catalogus → Producten → [product] → Maten & verpakking → EAN). Dit wordt automatisch meegenomen in bovenstaand template.

7 veelgemaakte fouten (en hoe je ze vermijdt)

Op basis van duizenden scans zien we telkens dezelfde JSON-LD fouten terugkomen. Hier zijn de zeven meest impactvolle:

1. Prijs als getal in plaats van string

Fout: "price": 49.95Goed: "price": "49.95". Schema.org verwacht een string voor prijs. Google's Rich Results Test geeft een warning bij numerieke waardes.

2. Missende priceValidUntil

Google vereist dit veld om pijlen bij prijzen in Rich Results te tonen. Stel minimaal in op een datum in de toekomst (bijv. "priceValidUntil": "2026-12-31"). Vergeet niet dit jaarlijks te updaten of dynamisch te genereren.

3. Availability URL verkeerd gespeld

Dit moet exact https://schema.org/InStock zijn — met hoofdletter en zonder trailing slash. Fouten als http://schema.org/in-stock of InStock worden niet herkend.

4. Lege of te korte description

Een product description van 10 woorden helpt AI niets. Richt je op minimaal 150 woorden per product. AI-tools citeren pagina's met uitgebreide, informatieve beschrijvingen significant vaker.

5. Reviews in HTML maar niet in JSON-LD

Als je Trustpilot, Google Reviews of eigen reviews hebt, maar ze niet opneemt in je JSON-LD, mist AI die informatie volledig. Voeg aggregateRating toe zodra je ook maar één review hebt.

6. Dubbele schema markup

Als je zowel een SEO-plugin (Yoast, RankMath) als custom code gebruikt, krijg je twee conflicterende Product blokken. Google kiest er één — niet altijd de juiste. Zet de plugin-output uit of gebruik uitsluitend de plugin met customisaties via hooks.

7. GTIN leeglaten of 0

Sommige implementaties voegen "gtin13": "0" of "gtin13": "" toe als er geen EAN bekend is. Dit schaadt meer dan het helpt. Voeg het veld alleen toe als er een valide 13-cijferig EAN is.

Let op bij variabele producten: Als jouw product varianten heeft (maat, kleur), moet elke variant zijn eigen Offer hebben met bijbehorende prijs en availability. Een enkel Offer voor alle varianten is technisch onjuist en kan leiden tot fouten in Google Search Console.

Hoe je je markup test en valideert

Na implementatie wil je weten of het werkt. Er zijn twee niveaus van testen:

Niveau 1: Google Rich Results Test

Ga naar search.google.com/test/rich-results en voer je product-URL in. Je ziet direct:

Dit test alleen Google-compatibiliteit — niet hoe AI-tools als ChatGPT je pagina zien.

Niveau 2: GEO Score scan

Voor een volledig beeld — inclusief hoe ChatGPT, Perplexity en Google AI Overviews je pagina interpreteren — gebruik de gratis GEO Tool scan. Die controleert niet alleen structured data maar ook alle 5 GEO-dimensies.

Gratis

Scan je productpagina nu

Ontdek je GEO-score op alle 5 dimensies — inclusief structured data analyse. Geen account vereist.

Gratis scan starten →

Veelgestelde vragen

Wat is JSON-LD en waarom heb ik het nodig?

JSON-LD (JavaScript Object Notation for Linked Data) is de aanbevolen manier van Google en schema.org om gestructureerde data aan webpagina's toe te voegen. Het vertelt zoekmachines én AI-tools precies wat een product kost, welke reviews het heeft en wat de eigenschappen zijn — zonder dat ze de HTML hoeven te "raden". Resultaat: rijke resultaten in Google, hogere GEO-score en meer kans op vermeldingen in ChatGPT.

Wat is het verschil tussen JSON-LD en Microdata?

JSON-LD staat los van de HTML-structuur en is makkelijker te onderhouden. Microdata is ingebouwd in de HTML-tags zelf. Google geeft de voorkeur aan JSON-LD. Voor nieuwe implementaties is JSON-LD altijd de betere keuze — het is makkelijker te testen, te debuggen en te automatiseren.

Welke JSON-LD velden zijn verplicht voor Google Rich Results?

Voor Google Product Rich Results zijn verplicht: name, image, en minimaal één van: offers (met price en priceCurrency), aggregateRating, of review. Voor maximale GEO-score voeg je ook toe: description, sku, brand, gtin13 (EAN), en availability.

Moet ik JSON-LD voor elke productpagina handmatig invullen?

Nee. De platform-specifieke code in dit artikel genereert automatisch JSON-LD voor elke productpagina vanuit je bestaande productdata. Eenmalige implementatie, directe werking op alle producten.

Hoe test ik of mijn JSON-LD correct is?

Gebruik de Google Rich Results Test (search.google.com/test/rich-results) voor Google-compatibiliteit. Voor een volledige GEO-score analyse — inclusief hoe ChatGPT en Perplexity je pagina zien — gebruik de GEO Tool gratis scan.