Je winkelwagen is leeg
Producten die je toevoegt, verschijnen hier.
WordPress SEO-plugin vervangen door Schema.org markup: minder plugin-overhead, JSON-LD zelf beheren. Stappenplan, code en veelgemaakte fouten.
Samenvatting: SEO-plugin vervangen door Schema.org markup
Google noemt JSON-LD zelf het aanbevolen formaat voor structured data[1]. Handmatige JSON-LD via functions.php weegt doorgaans minder dan 1KB[2], tegenover bijna 15MB aan pluginbestanden voor Yoast SEO alleen al[3]. Dit artikel laat zien wanneer die wissel loont, hoe je de code veilig plaatst via een child theme, en hoe je dubbele schema met een draaiende SEO-plugin voorkomt.
Yoast SEO draait op 30,4% van alle Nederlandse WordPress
KeurigOnline zegtWordPress
KeurigOnline zegtDatabase
KeurigOnline zegtPHP
Hieronder lees je wat Schema.org markup precies doet, wanneer handmatig beheer verstandig is, en hoe je het stap voor stap bouwt zonder dat je bij de volgende thema-update alles kwijtraakt.

Schema.org is een gedeeld vocabulaire waarmee je aan zoekmachines vertelt wát er op een pagina staat, niet alleen dát er tekst staat. Een blogartikel wordt een Article, je bedrijf een Organization, een navigatiepad een BreadcrumbList. Google leest die markup en zet ze soms om in rich results: sterrenbeoordelingen, broodkruimels in de zoekresultaten, of een uitklapbare FAQ.
Er bestaan drie manieren om die markup te schrijven: JSON
KeurigOnline zegtJSON
KeurigOnline zegtHTML<script>-blok[6]. Je voegt het toe zonder je template-HTML aan te raken, en zonder dat een plugin ertussen hoeft te zitten.
Een SEO-plugin genereert die JSON-LD dynamisch bij elke paginaload, verstopt in een instellingenscherm dat je zelden opent. Jij kunt exact hetzelfde script zelf schrijven, met een fractie van de overhead.
Yoast SEO is wereldwijd goed voor 69,5% van alle SEO-plugins en draait op 45,6% van alle WordPress-sites[7]; Rank Math volgt met 13,1% wereldwijd marktaandeel[8]. Beide zijn goede plugins, maar ze wegen wat.
Een onafhankelijke benchmark op GeneratePress met PHP 8.2 liet zien dat Yoast Free ongeveer 33KB paginagewicht en 148ms Total Blocking Time toevoegt bovenop een kale baseline van 90ms; Rank Math Free bleef bij ongeveer 12KB en 92ms[9]. WP Rocket's eigen cijfers laten een vergelijkbaar verschil zien in bestandsgrootte: 11,8MB voor Rank Math tegenover 32,4MB voor Yoast[9].
Een aparte plugin-vergelijking zet dat verschil nog scherper neer: een minimale schema-plugin van 186KB haalt gemiddeld 132ms TTFB
KeurigOnline zegtTTFB
KeurigOnline zegtShared hostingfunctions.php weegt daarentegen minder dan 1KB en heeft vrijwel geen meetbare invloed op de laadtijd[2].
Plugin-bloat is een bekende oorzaak van trage WordPress-sites, zoals we eerder beschreven in 5 redenen waarom je WordPress site zo langzaam is: elke module die je uitschakelt, telt mee. Zie ook hoe plugins je Core Web Vitals beïnvloeden voor het bredere plaatje.
Handmatige JSON
KeurigOnline zegtJSON
Bij een webshop met WooCommerce
KeurigOnline zegtWooCommerce
De vuistregel: hoe voorspelbaarder je contentstructuur, hoe meer een handmatige aanpak oplevert. Hoe meer variatie en niet-technisch beheer, hoe meer een plugin zijn overhead terugverdient.
Voeg je code rechtstreeks toe aan het functions.php-bestand van je actieve thema, dan verdwijnt die code zodra het thema een update krijgt: WordPress overschrijft themabestanden volledig bij elke update. Zonder child theme ben je dus na de eerstvolgende thema-update je schema markup kwijt, zonder waarschuwing[2].
Je hebt twee veilige opties:
functions.php die de code van het hoofdthema aanvult. De standaardkeuze als je verder ook kleine visuele aanpassingen doet.wp-content/mu-plugins/. Dit werkt volledig thema-onafhankelijk, blijft actief als je later van thema wisselt, en kan niet per ongeluk worden uitgeschakeld.Twijfel je of je ooit van thema wisselt? Kies de mu-plugin: je schema markup hoort bij je site, niet bij je vormgeving. Wil je dieper in de WordPress-architectuur duiken, bijvoorbeeld hoe hooks zich verhouden tot moderne uitbreidingsmechanismen, lees dan onze uitleg over de WordPress Abilities API.
Eén functie die op wp_head haakt en één <script>-blok print, werkt voor één schema-type. Zodra je Organization, WebSite, Article, breadcrumbs én FAQ tegelijk wilt uitleveren, eindig je met vijf losse scripts in de <head>, die je stuk voor stuk moet onderhouden en die elkaar makkelijk in de weg zitten. Slimmer: bouw één centrale @graph, waarin alle schema-nodes van de pagina via een @id naar elkaar verwijzen, en laat elk type zichzelf daarin registreren via een eigen WordPress-filter. Zo voeg je later een nieuw type toe zonder ooit de kernfunctie aan te raken.
Eerst de motor: één functie die alle geregistreerde schema-nodes verzamelt via het filter ko_schema_graph en ze in één JSON-LD-blok uitprint.
// In functions.php van je child theme, of in een mu-plugin
/**
* Verwijdert lege/null-velden uit een schema-node, zodat je nooit
* een ongeldig "image": null of "sku": "" in de output krijgt.
*/
function ko_strip_empty( array $node ) {
return array_filter(
$node,
static function ( $value ) {
return null !== $value && '' !== $value && array() !== $value;
}
);
}
/**
* De motor: verzamelt alle geregistreerde schema-nodes en print ze
* als één @graph. Andere functies breiden dit uit via het
* 'ko_schema_graph' filter, zonder deze functie te wijzigen.
*/
function ko_render_schema_graph() {
$graph = apply_filters( 'ko_schema_graph', array() );
$graph = array_values( array_filter( $graph ) );
if ( empty( $graph ) ) {
return;
}
$document = array(
'@context' => 'https://schema.org',
'@graph' => $graph,
);
echo '<script type="application/ld+json">' .
wp_json_encode( $document, JSON_UNESCAPED_SLASHES ) .
'</script>' . "\n";
}
add_action( 'wp_head', 'ko_render_schema_graph' );
Nu registreer je elk schema-type als een losse functie: zelf inhaken op het filter, een eigen paginaconditie bewaken en dynamische velden teruggeven via wp_json_encode() in plaats van hardcoded tekst[11]. De Organization-node, sitebreed op de homepage:
add_filter( 'ko_schema_graph', 'ko_schema_organization' );
function ko_schema_organization( $graph ) {
if ( ! is_front_page() ) {
return $graph;
}
$graph[] = ko_strip_empty( array(
'@type' => 'Organization',
'@id' => home_url( '/#organization' ),
'name' => get_bloginfo( 'name' ),
'url' => home_url( '/' ),
'logo' => get_site_icon_url(),
) );
return $graph;
}
Elke volgende builder in dit artikel volgt exact dit patroon: add_filter( 'ko_schema_graph', ... ), een conditie die bepaalt of het type op déze pagina thuishoort, en velden die met WordPress- of WooCommerce-functies worden ingevuld in plaats van met tekst die je per pagina zou moeten aanpassen. Voeg je later een zesde of zevende type toe, dan plak je alleen een nieuwe functie onder de rest, de motor blijft ongewijzigd.
Je hoeft niet elk Schema.org-type te implementeren, maar met alleen Organization en Article mis je context waar Google om vraagt: een zoekbalk in de resultaten, een broodkruimelpad, of een productprijs. De vijf builders hieronder dekken samen wat de meeste bedrijfssites, blogs en webshops echt gebruiken, elk als losse functie op hetzelfde ko_schema_graph-filter uit de vorige stap[11]. Vul verplichte velden altijd echt in: een Article heeft minimaal headline, image, datePublished en author nodig, een Product heeft name, image en offers nodig[12]. Niet elke pagina heeft schema nodig: een privacyverklaring of 404-pagina voegt niets toe aan een rich result.
Heeft je site een zoekfunctie, dan kan Google die als sitelinks-zoekvak tonen. De conditie is identiek aan die van Organization, maar het is een apart type met een eigen @id.
add_filter( 'ko_schema_graph', 'ko_schema_website' );
function ko_schema_website( $graph ) {
if ( ! is_front_page() ) {
return $graph;
}
$graph[] = array(
'@type' => 'WebSite',
'@id' => home_url( '/#website' ),
'name' => get_bloginfo( 'name' ),
'url' => home_url( '/' ),
'potentialAction' => array(
'@type' => 'SearchAction',
'target' => home_url( '/?s={search_term_string}' ),
'query-input' => 'required name=search_term_string',
),
);
return $graph;
}
Een broodkruimelpad hardcoden per pagina is precies het soort onderhoud dat je met handmatige schema wilt vermijden. Deze builder leest in plaats daarvan de categorie van een artikel of de paginaboom van een pagina uit, en bouwt het pad zelf op, voor elke pagina anders, zonder dat je ooit een array met de hand aanpast.
add_filter( 'ko_schema_graph', 'ko_schema_breadcrumbs' );
function ko_schema_breadcrumbs( $graph ) {
if ( is_front_page() ) {
return $graph;
}
$items = array();
$items[] = array( __( 'Home', 'ko' ), home_url( '/' ) );
if ( is_single() ) {
$cats = get_the_category();
if ( ! empty( $cats ) ) {
$items[] = array( $cats[0]->name, get_category_link( $cats[0]->term_id ) );
}
$items[] = array( get_the_title(), get_permalink() );
} elseif ( is_page() ) {
foreach ( array_reverse( get_post_ancestors( get_the_ID() ) ) as $ancestor_id ) {
$items[] = array( get_the_title( $ancestor_id ), get_permalink( $ancestor_id ) );
}
$items[] = array( get_the_title(), get_permalink() );
} else {
return $graph; // archieven e.d. hebben geen vast pad, sla over
}
$list_items = array();
foreach ( $items as $position => $item ) {
$list_items[] = array(
'@type' => 'ListItem',
'position' => $position + 1,
'name' => $item[0],
'item' => $item[1],
);
}
$graph[] = array(
'@type' => 'BreadcrumbList',
'itemListElement' => $list_items,
);
return $graph;
}
In plaats van een losse publisher met alle Organization-velden opnieuw te schrijven, verwijst deze builder via @id terug naar de Organization-node van de homepage. Google leest de relatie via die verwijzing, jij hoeft niets te dupliceren.
add_filter( 'ko_schema_graph', 'ko_schema_article' );
function ko_schema_article( $graph ) {
if ( ! is_single() ) {
return $graph;
}
$image_id = get_post_thumbnail_id();
$graph[] = ko_strip_empty( array(
'@type' => 'BlogPosting',
'headline' => get_the_title(),
'image' => $image_id ? wp_get_attachment_image_url( $image_id, 'full' ) : null,
'author' => array(
'@type' => 'Person',
'name' => get_the_author(),
),
'publisher' => array( '@id' => home_url( '/#organization' ) ),
'datePublished' => get_the_date( 'c' ),
'dateModified' => get_the_modified_date( 'c' ),
'mainEntityOfPage' => get_permalink(),
) );
return $graph;
}
Draait je site op WooCommerce, dan haalt deze builder prijs, valuta, voorraadstatus en beoordelingen rechtstreeks uit het productobject, dus altijd actueel, nooit een vergeten prijswijziging in een losse schema-array. aggregateRating wordt alleen toegevoegd als er daadwerkelijk beoordelingen zijn: een reviewCount van 0 meesturen levert ongeldige schema op[12].
add_filter( 'ko_schema_graph', 'ko_schema_product' );
function ko_schema_product( $graph ) {
if ( ! function_exists( 'is_product' ) || ! is_product() ) {
return $graph; // WooCommerce niet actief, of geen productpagina
}
global $product;
if ( ! $product instanceof WC_Product ) {
return $graph;
}
$node = ko_strip_empty( array(
'@type' => 'Product',
'name' => $product->get_name(),
'image' => wp_get_attachment_image_url( $product->get_image_id(), 'full' ),
'sku' => $product->get_sku(),
'offers' => array(
'@type' => 'Offer',
'priceCurrency' => get_woocommerce_currency(),
'price' => $product->get_price(),
'availability' => $product->is_in_stock()
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock',
'url' => get_permalink( $product->get_id() ),
),
) );
if ( $product->get_review_count() > 0 ) {
$node['aggregateRating'] = array(
'@type' => 'AggregateRating',
'ratingValue' => $product->get_average_rating(),
'reviewCount' => $product->get_review_count(),
);
}
$graph[] = $node;
return $graph;
}
De meeste handleidingen laten je de FAQ-schema handmatig overtypen naast de zichtbare vraag en antwoord op de pagina, met als gevolg dat de twee na een tekstwijziging uit elkaar gaan lopen (precies de mismatch waar Google op afrekent, zie hierna). Slimmer: lees de FAQ-items rechtstreeks uit de gerenderde content, met DOMDocument, zodat schema en zichtbare tekst per definitie gelijk zijn en je nooit iets dubbel onderhoudt. Deze builder zoekt naar dezelfde .ai-faq-item-structuur die dit artikel voor zijn eigen FAQ-sectie gebruikt.
add_filter( 'ko_schema_graph', 'ko_schema_faq_from_content' );
function ko_schema_faq_from_content( $graph ) {
if ( ! is_single() ) {
return $graph;
}
$content = get_the_content();
if ( false === strpos( $content, 'ai-faq-item' ) ) {
return $graph; // geen FAQ-blok op deze pagina
}
$dom = new DOMDocument();
libxml_use_internal_errors( true ); // negeer HTML5-warnings van DOMDocument
$dom->loadHTML( '<?xml encoding="utf-8" ?>' . apply_filters( 'the_content', $content ) );
libxml_clear_errors();
$xpath = new DOMXPath( $dom );
$items = $xpath->query( "//div[contains(@class,'ai-faq-item')]" );
$questions = array();
foreach ( $items as $item ) {
$q = $xpath->query( './/h3', $item )->item( 0 );
$a = $xpath->query( ".//p[contains(@class,'with-margin')]", $item )->item( 0 );
if ( ! $q || ! $a ) {
continue;
}
$questions[] = array(
'@type' => 'Question',
'name' => trim( $q->textContent ),
'acceptedAnswer' => array(
'@type' => 'Answer',
'text' => trim( $a->textContent ),
),
);
}
if ( empty( $questions ) ) {
return $graph;
}
$graph[] = array(
'@type' => 'FAQPage',
'mainEntity' => $questions,
);
return $graph;
}
Laat je Yoast of Rank Math actief draaien voor andere taken, en voeg je zelf ook JSON-LD toe, dan ontstaat al snel dubbele of conflicterende schema: twee Organization-blokken, of een Article vanuit de plugin naast jouw eigen versie[12]. Google negeert dat niet automatisch; het kan de parser in verwarring brengen en rich results juist blokkeren in plaats van opleveren.
Controleer dit altijd voordat je zelf iets toevoegt:
application/ld+json.Herhaal deze check na elke grote plugin-update: instellingen die je hebt uitgeschakeld, worden bij sommige updates stilletjes teruggezet.
Publiceer nooit schema markup zonder te testen. Twee gratis tools zijn hiervoor het startpunt:
Krijg je een foutmelding over een ontbrekend verplicht veld, ga dan terug naar je PHP-array en vul het aan: vrijwel elke foutmelding wijst naar een specifiek veld dat je bent vergeten. Krijg je een waarschuwing over een mismatch tussen zichtbare content en markup, bijvoorbeeld een auteursnaam in de schema die niet op de pagina zelf staat, pas dan de zichtbare tekst aan of corrigeer de schema. Nooit allebei los van elkaar laten staan.
De cijfers uit het begin van dit artikel vertalen zich naar een concreet voor/na-beeld. Vóór de wissel: een SEO-plugin die 12 tot 33KB extra paginagewicht en 92 tot 148ms Total Blocking Time toevoegt[9], met bij sommige plugins tot 15 extra databasequeries en 200 tot 400ms extra TTFB per paginaload[10]. Ná de wissel: een <script>-blok van minder dan 1KB, gegenereerd door PHP dat al bij elke paginaload draait, zonder extra databasequeries[2].
Dat verschil is het grootst op shared hosting
KeurigOnline zegtShared hosting
KeurigOnline zegtPHP
Article zonder datePublished of een Product zonder offers kwalificeert simpelweg niet voor een rich result[12].get_the_title() en get_the_date(), waardoor de schema bij het volgende artikel niet meer klopt.Je hebt nu zes builders gezien: elk registreert zichzelf bij het ko_schema_graph-filter, bewaakt zijn eigen paginaconditie en levert alleen dynamische velden aan. Hieronder staan ze in één werkend bestand, in de volgorde waarin je ze hierboven tegenkwam, zodat je niet zelf hoeft te knippen en plakken uit losse codeblokken.
Kort overzicht van wat elke builder dekt en waar hij actief wordt:
@id..ai-faq-item-blok, rechtstreeks uit de zichtbare content gelezen, dus nooit los van de tekst op de pagina.Kopieer dit hele blok in één nieuw bestand. Laat de <?php-openingstag bovenaan staan als je een los bestand aanmaakt (optie A hieronder); laat hem juist weg als je de code onderaan een bestaande functions.php plakt (optie B).
<?php
/**
* Schema.org JSON-LD voor deze site.
* Bouwt één @graph met alle onderstaande types, gehaakt op wp_head.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Directe toegang tot dit bestand blokkeren.
}
/**
* Verwijdert lege/null-velden uit een schema-node, zodat je nooit
* een ongeldig "image": null of "sku": "" in de output krijgt.
*/
function ko_strip_empty( array $node ) {
return array_filter(
$node,
static function ( $value ) {
return null !== $value && '' !== $value && array() !== $value;
}
);
}
/**
* De motor: verzamelt alle geregistreerde schema-nodes en print ze
* als één @graph. Andere functies breiden dit uit via het
* 'ko_schema_graph' filter, zonder deze functie te wijzigen.
*/
function ko_render_schema_graph() {
$graph = apply_filters( 'ko_schema_graph', array() );
$graph = array_values( array_filter( $graph ) );
if ( empty( $graph ) ) {
return;
}
$document = array(
'@context' => 'https://schema.org',
'@graph' => $graph,
);
echo '<script type="application/ld+json">' .
wp_json_encode( $document, JSON_UNESCAPED_SLASHES ) .
'</script>' . "\n";
}
add_action( 'wp_head', 'ko_render_schema_graph' );
/** 1. Organization, sitebreed op de homepage. */
add_filter( 'ko_schema_graph', 'ko_schema_organization' );
function ko_schema_organization( $graph ) {
if ( ! is_front_page() ) {
return $graph;
}
$graph[] = ko_strip_empty( array(
'@type' => 'Organization',
'@id' => home_url( '/#organization' ),
'name' => get_bloginfo( 'name' ),
'url' => home_url( '/' ),
'logo' => get_site_icon_url(),
) );
return $graph;
}
/** 2. WebSite, met zoekactie op de homepage. */
add_filter( 'ko_schema_graph', 'ko_schema_website' );
function ko_schema_website( $graph ) {
if ( ! is_front_page() ) {
return $graph;
}
$graph[] = array(
'@type' => 'WebSite',
'@id' => home_url( '/#website' ),
'name' => get_bloginfo( 'name' ),
'url' => home_url( '/' ),
'potentialAction' => array(
'@type' => 'SearchAction',
'target' => home_url( '/?s={search_term_string}' ),
'query-input' => 'required name=search_term_string',
),
);
return $graph;
}
/** 3. BreadcrumbList, dynamisch per pagina opgebouwd. */
add_filter( 'ko_schema_graph', 'ko_schema_breadcrumbs' );
function ko_schema_breadcrumbs( $graph ) {
if ( is_front_page() ) {
return $graph;
}
$items = array();
$items[] = array( __( 'Home', 'ko' ), home_url( '/' ) );
if ( is_single() ) {
$cats = get_the_category();
if ( ! empty( $cats ) ) {
$items[] = array( $cats[0]->name, get_category_link( $cats[0]->term_id ) );
}
$items[] = array( get_the_title(), get_permalink() );
} elseif ( is_page() ) {
foreach ( array_reverse( get_post_ancestors( get_the_ID() ) ) as $ancestor_id ) {
$items[] = array( get_the_title( $ancestor_id ), get_permalink( $ancestor_id ) );
}
$items[] = array( get_the_title(), get_permalink() );
} else {
return $graph; // archieven e.d. hebben geen vast pad, sla over
}
$list_items = array();
foreach ( $items as $position => $item ) {
$list_items[] = array(
'@type' => 'ListItem',
'position' => $position + 1,
'name' => $item[0],
'item' => $item[1],
);
}
$graph[] = array(
'@type' => 'BreadcrumbList',
'itemListElement' => $list_items,
);
return $graph;
}
/** 4. Article/BlogPosting, gekoppeld aan de Organization-node. */
add_filter( 'ko_schema_graph', 'ko_schema_article' );
function ko_schema_article( $graph ) {
if ( ! is_single() ) {
return $graph;
}
$image_id = get_post_thumbnail_id();
$graph[] = ko_strip_empty( array(
'@type' => 'BlogPosting',
'headline' => get_the_title(),
'image' => $image_id ? wp_get_attachment_image_url( $image_id, 'full' ) : null,
'author' => array(
'@type' => 'Person',
'name' => get_the_author(),
),
'publisher' => array( '@id' => home_url( '/#organization' ) ),
'datePublished' => get_the_date( 'c' ),
'dateModified' => get_the_modified_date( 'c' ),
'mainEntityOfPage' => get_permalink(),
) );
return $graph;
}
/** 5. Product, met live prijs en voorraad uit WooCommerce. */
add_filter( 'ko_schema_graph', 'ko_schema_product' );
function ko_schema_product( $graph ) {
if ( ! function_exists( 'is_product' ) || ! is_product() ) {
return $graph; // WooCommerce niet actief, of geen productpagina
}
global $product;
if ( ! $product instanceof WC_Product ) {
return $graph;
}
$node = ko_strip_empty( array(
'@type' => 'Product',
'name' => $product->get_name(),
'image' => wp_get_attachment_image_url( $product->get_image_id(), 'full' ),
'sku' => $product->get_sku(),
'offers' => array(
'@type' => 'Offer',
'priceCurrency' => get_woocommerce_currency(),
'price' => $product->get_price(),
'availability' => $product->is_in_stock()
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock',
'url' => get_permalink( $product->get_id() ),
),
) );
if ( $product->get_review_count() > 0 ) {
$node['aggregateRating'] = array(
'@type' => 'AggregateRating',
'ratingValue' => $product->get_average_rating(),
'reviewCount' => $product->get_review_count(),
);
}
$graph[] = $node;
return $graph;
}
/** 6. FAQPage, automatisch uit .ai-faq-item-blokken in de content. */
add_filter( 'ko_schema_graph', 'ko_schema_faq_from_content' );
function ko_schema_faq_from_content( $graph ) {
if ( ! is_single() ) {
return $graph;
}
$content = get_the_content();
if ( false === strpos( $content, 'ai-faq-item' ) ) {
return $graph; // geen FAQ-blok op deze pagina
}
$dom = new DOMDocument();
libxml_use_internal_errors( true ); // negeer HTML5-warnings van DOMDocument
$dom->loadHTML( '<?xml encoding="utf-8" ?>' . apply_filters( 'the_content', $content ) );
libxml_clear_errors();
$xpath = new DOMXPath( $dom );
$items = $xpath->query( "//div[contains(@class,'ai-faq-item')]" );
$questions = array();
foreach ( $items as $item ) {
$q = $xpath->query( './/h3', $item )->item( 0 );
$a = $xpath->query( ".//p[contains(@class,'with-margin')]", $item )->item( 0 );
if ( ! $q || ! $a ) {
continue;
}
$questions[] = array(
'@type' => 'Question',
'name' => trim( $q->textContent ),
'acceptedAnswer' => array(
'@type' => 'Answer',
'text' => trim( $a->textContent ),
),
);
}
if ( empty( $questions ) ) {
return $graph;
}
$graph[] = array(
'@type' => 'FAQPage',
'mainEntity' => $questions,
);
return $graph;
}
Waar je dit precies neerzet (de achtergrond staat in Voorbereiding hierboven):
<?php-regel in een nieuw bestand wp-content/mu-plugins/ko-schema-markup.php. Bestaat de map mu-plugins nog niet, maak hem dan aan. WordPress<?php-regel bovenaan, die staat al in je bestaande bestand) onderaan de functions.php van je child theme.Na het plaatsen: bezoek de homepage en een los artikel, bekijk de paginabron en zoek op application/ld+json om te controleren of het @graph-blok verschijnt. Heb je WooCommerce, controleer dan ook een productpagina. Loop daarna de stappen uit Testen en valideren hierboven na voordat je de bijbehorende schema-module van je SEO-plugin uitschakelt.
Handmatige JSON-LD via functions.php weegt doorgaans minder dan 1KB en heeft vrijwel geen meetbare invloed op de laadtijd[2]. Een zware SEO-plugin die schema dynamisch en ongecachet opbouwt, kan wel 40 tot 80KB en honderden milliseconden aan TTFB
KeurigOnline zegtTTFB
JSON-LD staat los van je zichtbare HTML in een eigen <script>-blok; Microdata en RDFa moet je middenin je bestaande HTML
KeurigOnline zegtHTML
KeurigOnline zegtJSON
Voor schema markup alleen: ja, mits je sitestructuur voorspelbaar is (blog, bedrijfssite). Voor de rest van wat een SEO-plugin doet, zoals meta titles, sitemaps en redirects, is een plugin meestal nog steeds de praktischste keuze, zeker bij WooCommerce
KeurigOnline zegtWooCommerce
Nooit in het functions.php of header.php van je hoofdthema: die worden overschreven bij een thema-update. Gebruik een child theme voor site-specifieke code, of een mu-plugin in wp-content/mu-plugins/ als je thema-onafhankelijkheid wilt[2].
Dan ontstaat dubbele of conflicterende schema voor hetzelfde contenttype[12]. Controleer altijd eerst via "bekijk paginabron" of application/ld+json al aanwezig is, en schakel de schema-module van je plugin uit voordat je zelf iets toevoegt.
Organization en WebSite sitebreed, Article of BlogPosting op elk artikel, en BreadcrumbList op pagina's met een navigatiepad[11]. FAQ-rich results worden sinds 2023 door Google grotendeels beperkt tot overheids- en gezondheidssites, maar de markup blijft waardevol voor AI-antwoordsystemen[13].