SEO plugins handle schema markup fine for most sites, but they generate it generically, which sometimes means fields that don’t quite fit your actual content, or schema that duplicates what a page builder is already outputting. Writing it directly gives you exact control over what search engines see.
Breadcrumb schema
add_action('wp_head', function() {
if (!is_singular()) return;
$breadcrumbs = [
['name' => 'Home', 'url' => home_url('/')],
];
if (is_singular('post')) {
$cats = get_the_category();
if ($cats) {
$breadcrumbs[] = ['name' => $cats[0]->name, 'url' => get_category_link($cats[0]->term_id)];
}
}
$breadcrumbs[] = ['name' => get_the_title(), 'url' => get_permalink()];
$items = [];
foreach ($breadcrumbs as $i => $b) {
$items[] = [
'@type' => 'ListItem',
'position' => $i + 1,
'name' => $b['name'],
'item' => $b['url'],
];
}
$schema = [
'@context' => 'https://schema.org',
'@type' => 'BreadcrumbList',
'itemListElement' => $items,
];
echo '<script type="application/ld+json">' . wp_json_encode($schema) . '</script>';
});
Article schema
add_action('wp_head', function() {
if (!is_single()) return;
$schema = [
'@context' => 'https://schema.org',
'@type' => 'Article',
'headline' => get_the_title(),
'datePublished' => get_the_date('c'),
'dateModified' => get_the_modified_date('c'),
'author' => ['@type' => 'Person', 'name' => get_the_author()],
'image' => get_the_post_thumbnail_url(get_the_ID(), 'large') ?: '',
];
echo '<script type="application/ld+json">' . wp_json_encode($schema) . '</script>';
});
Verifying it’s actually valid
Google’s Rich Results Test checks whether the schema is well-formed and eligible for the rich result types it targets. It’s worth running this after any change, since a typo in a JSON-LD block doesn’t break the page visually — it just silently fails to produce the rich snippet you were trying to get, with no visible error anywhere.
The one real risk with hand-written schema
If your SEO plugin is already outputting article or breadcrumb schema and you add your own on top, you now have duplicate, potentially conflicting schema on the same page — which Google’s structured data testing tools will flag. Check what your existing plugin already outputs before adding custom schema for the same content type.