Skip to main content

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:

SettingValue
Insert MethodAuto Insert
LocationFrontend 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

  1. Toggle the snippet to Active
  2. Make sure Testing Mode is off
  3. Click Save Changes

The snippet applies automatically to every public WordPress page and post — no shortcodes or per-page configuration needed.


Step 5: Verify

  1. Open any public page (not the WordPress admin)
  2. Right-click → View Page Source
  3. 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 TypeAppears onDescription
OrganizationAll pagesSite name, URL, and logo
WebSiteAll pagesSite-level entity with publisher reference
WebPageWordPress PagesPage title, description, dates, breadcrumb
ArticleWordPress PostsHeadline, image, publisher, dates, breadcrumb
BreadcrumbListAll singular pagesPage hierarchy trail from home to current page