Adding JSON-LD Structured Data to WordPress
JSON-LD (JavaScript Object Notation for Linked Data) is the recommended way to add structured data to web pages. Google uses it to understand what a page is about and can display rich results in search. For AI crawlers (ChatGPT, Perplexity, Gemini), structured data helps them accurately identify and cite your content.
The correct way to add JSON-LD to WordPress is via a PHP code snippet hooked into wp_head — not by editing WordPress core files.
Before You Start
Check whether Yoast SEO, RankMath, AIOSEO, or another SEO plugin is already generating schema on your site. Running two schema sources simultaneously will produce conflicting JSON-LD. If an SEO plugin is active, either disable its schema output or skip this guide.
To check: open any public page → View Page Source → search for application/ld+json. If you already see schema output, identify the source before adding more.
Step 1: Open Code Snippets
In WordPress Admin, go to:
Code Snippets → Add New
If you don't have the Code Snippets plugin, install it from Plugins → Add New (search "Code Snippets" by Code Snippets Pro).
Step 2: Create the Snippet
- Name:
Generate JSON-LD for Pages - Code Type: PHP Snippet
- Insertion: Auto Insert → Frontend Only
Paste the following code:
add_action('wp_head', 'ai12z_output_jsonld', 20);
function ai12z_output_jsonld() {
if (is_admin() || is_feed() || is_404()) {
return;
}
$home_url = home_url('/');
$site_name = get_bloginfo('name');
$language = get_bloginfo('language');
$logo_id = get_theme_mod('custom_logo');
$logo_url = $logo_id ? wp_get_attachment_image_url($logo_id, 'full') : '';
$graph = array();
$organization = array(
'@type' => 'Organization',
'@id' => $home_url . '#organization',
'name' => $site_name,
'url' => $home_url,
);
if ($logo_url) {
$organization['logo'] = array(
'@type' => 'ImageObject',
'url' => $logo_url,
);
}
$graph[] = $organization;
$graph[] = array(
'@type' => 'WebSite',
'@id' => $home_url . '#website',
'url' => $home_url,
'name' => $site_name,
'publisher' => array('@id' => $home_url . '#organization'),
'inLanguage' => $language,
);
if (is_singular()) {
$post_id = get_queried_object_id();
$url = get_permalink($post_id);
$title = wp_strip_all_tags(get_the_title($post_id));
$desc = ai12z_jsonld_description($post_id);
$schema_type = is_singular('post') ? 'Article' : 'WebPage';
$page = array(
'@type' => $schema_type,
'@id' => $url . '#webpage',
'url' => $url,
'name' => $title,
'description' => $desc,
'isPartOf' => array('@id' => $home_url . '#website'),
'about' => array('@id' => $home_url . '#organization'),
'breadcrumb' => array('@id' => $url . '#breadcrumb'),
'inLanguage' => $language,
'datePublished' => get_the_date('c', $post_id),
'dateModified' => get_the_modified_date('c', $post_id),
);
if ($schema_type === 'Article') {
$page['headline'] = $title;
$page['publisher'] = array('@id' => $home_url . '#organization');
$page['mainEntityOfPage'] = array('@id' => $url . '#webpage');
}
if (has_post_thumbnail($post_id)) {
$image_url = get_the_post_thumbnail_url($post_id, 'full');
if ($image_url) {
$page['primaryImageOfPage'] = array(
'@type' => 'ImageObject',
'url' => $image_url,
);
if ($schema_type === 'Article') {
$page['image'] = $image_url;
}
}
}
$graph[] = $page;
$breadcrumb = ai12z_jsonld_breadcrumb($post_id);
if ($breadcrumb) {
$graph[] = $breadcrumb;
}
}
$jsonld = array(
'@context' => 'https://schema.org',
'@graph' => $graph,
);
echo "\n<script type=\"application/ld+json\">";
echo wp_json_encode($jsonld, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
echo "</script>\n";
}
function ai12z_jsonld_description($post_id) {
$excerpt = get_post_field('post_excerpt', $post_id);
if (!$excerpt) {
$content = get_post_field('post_content', $post_id);
$excerpt = wp_trim_words(
wp_strip_all_tags(strip_shortcodes($content)),
35
);
}
return wp_strip_all_tags($excerpt);
}
function ai12z_jsonld_breadcrumb($post_id) {
$url = get_permalink($post_id);
$items = array();
$position = 1;
$items[] = array(
'@type' => 'ListItem',
'position' => $position++,
'name' => get_bloginfo('name'),
'item' => home_url('/'),
);
$ancestors = array_reverse(get_post_ancestors($post_id));
foreach ($ancestors as $ancestor_id) {
$items[] = array(
'@type' => 'ListItem',
'position' => $position++,
'name' => wp_strip_all_tags(get_the_title($ancestor_id)),
'item' => get_permalink($ancestor_id),
);
}
$items[] = array(
'@type' => 'ListItem',
'position' => $position++,
'name' => wp_strip_all_tags(get_the_title($post_id)),
'item' => $url,
);
return array(
'@type' => 'BreadcrumbList',
'@id' => $url . '#breadcrumb',
'itemListElement' => $items,
);
}
Step 3: Configure Insertion Settings
Scroll down to the Insertion section and set:
| Setting | Value |
|---|---|
| Insert Method | Auto Insert |
| Location | Frontend Only |
Why Frontend Only? The snippet already calls
add_action('wp_head', ...)which hooks into the front-end page<head>. Running it everywhere or on the admin side is unnecessary.
Step 4: Activate and Save
- Toggle the snippet to Active
- Make sure Testing Mode is off
- Click Save Changes
The snippet applies automatically to every public WordPress page and post — no shortcodes or per-page configuration needed.
Step 5: Verify
- Open any public page (not the WordPress admin)
- Right-click → View Page Source
- Search for
application/ld+json
You should see a block like this in the <head>:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "Organization",
"@id": "https://yoursite.com/#organization",
"name": "Your Site Name",
"url": "https://yoursite.com/"
},
{
"@type": "WebPage",
"@id": "https://yoursite.com/page/#webpage",
"url": "https://yoursite.com/page/",
"name": "Page Title",
...
}
]
}
</script>
If the block doesn't appear, clear your caches:
- SpeedyCache or your WordPress cache plugin
- CDN cache (if applicable)
- Browser cache (
Ctrl+Shift+R/Cmd+Shift+R)
Step 6: Test with Google's Rich Results Tool
Go to search.google.com/test/rich-results, enter a public page URL, and run the test. It will confirm the structured data is valid and show which schema types were detected.
What This Snippet Generates
Every public page receives a @graph array containing:
| Schema Type | Appears on | Description |
|---|---|---|
Organization | All pages | Site name, URL, and logo |
WebSite | All pages | Site-level entity with publisher reference |
WebPage | WordPress Pages | Page title, description, dates, breadcrumb |
Article | WordPress Posts | Headline, image, publisher, dates, breadcrumb |
BreadcrumbList | All singular pages | Page hierarchy trail from home to current page |