Structured Data (JSON-LD)
Beta. The @graph SEO NEO emits — Organization, WebSite, WebPage, optional Article, Person, and BreadcrumbList — how it's wired, how to configure it, and how to extend or disable it.
Beta feature. The JSON-LD@graphgenerator is included in 1.x and$page->seoneo->renderSchema()will return output, but the API shape, default graph composition, and hook surface are not yet committed for 1.0 stable and may change in point releases. For production sites that need structured data right now, hand-rolling JSON-LD via a hook onrenderHeadis the recommended path until the API stabilises.
What SEO NEO emits
When JSON-LD is enabled, SEO NEO appends a <script type="application/ld+json"> block containing a Schema.org @graph array. The graph uses canonical @id URIs to wire nodes together, so the page, site, author, and breadcrumb trail are a single linked entity rather than four separate blobs.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "Organization",
"@id": "https://example.com/#organization",
"name": "Your Site",
"url": "https://example.com/"
},
{
"@type": "WebSite",
"@id": "https://example.com/#website",
"publisher": { "@id": "https://example.com/#organization" },
"inLanguage": "en"
},
{
"@type": "WebPage",
"@id": "https://example.com/page/#webpage",
"name": "Page Title | Your Site",
"inLanguage": "en",
"isPartOf": { "@id": "https://example.com/#website" },
"datePublished": "2026-05-12T11:49:20+01:00",
"dateModified": "2026-05-14T15:37:31+01:00"
},
{
"@type": "BreadcrumbList",
"@id": "https://example.com/page/#breadcrumb",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com/" },
{ "@type": "ListItem", "position": 2, "name": "Section", "item": "https://example.com/section/" },
{ "@type": "ListItem", "position": 3, "name": "Page Title", "item": "https://example.com/page/" }
]
}
]
}
</script>
Default graph nodes
- Organization (or
Person,LocalBusiness,NewsMediaOrganization,EducationalOrganization— set via Publisher type in module config) — the site-wide publisher, withname,url, optionallogo,description, andsameAslinks. ChoosePersonfor personal-brand sites where the publisher is an individual; the node still appears in the same slot in the graph. - WebSite — the site itself, with a
publisherreference back to the Organization and the default-languageinLanguage. - WebPage — the current page, with
name,description,url,inLanguage,isPartOf,dateModified,datePublished, andprimaryImageOfPagewhen an OG image resolves. - Article — added on pages whose template appears in Article templates in module config (e.g.
blog_post,journal_post). Includesheadline,description,image,datePublished,dateModified, resolved author, publisher reference, andinLanguage. - Person — added on pages whose template appears in Person templates (defaults to
user, so PW User pages used as author bios are recognised). Includesname,description,image, andurl. - BreadcrumbList — added when Emit BreadcrumbList is enabled (on by default) and the page has at least one parent.
Configuration
All JSON-LD settings live in Modules > Configure > SeoNeo > Structured data (JSON-LD):
- Emit JSON-LD — master switch. Enabled by default. Article templates are blank by default; add templates when you want Article nodes.
- Pretty-print JSON-LD — on by default. Disable for minified production output.
- Publisher type — the
@typefor the Organization node. IncludesPersonfor solo / personal-brand sites. - Article templates — comma-separated template names that get an Article node (e.g.
blog_post,news_item). - Person templates — template names that get a Person node (default:
user). - Emit BreadcrumbList — adds the breadcrumb trail. On by default.
- Publisher name (per language) / Publisher description (per language) —
langname=valuemaps for multilingual Organization nodes. The@idURIs stay language-invariant (schema.org best practice); only the descriptive properties translate. - Publisher logo URL — absolute URL to a square logo image. Emitted as
logo.urlon the publisher node and aspublisher.logoon Article nodes. Required by Google for certain rich-result eligibility. - Publisher sameAs URLs — one URL per line. Used as the publisher's
sameAsarray — typically your social profiles (LinkedIn, X, GitHub, Crunchbase, Wikipedia). Adding these helps search engines build a confident knowledge-graph entity for your brand. - Default Article author — select a ProcessWire User page as fallback author on Article nodes. Pages with a
seoneo_authorfield override this.
Hooks
Two hookable entry points let you extend or override the graph per site, per template, or per page:
// Add a custom node to every page's graph
wire()->addHookAfter('SeoNeo::getJsonLd', function($event) {
$graph = $event->return;
$graph['@graph'][] = [
'@type' => 'FAQPage',
'mainEntity' => [/* your FAQ items */]
];
$event->return = $graph;
});
// Suppress the BreadcrumbList on a specific template
wire()->addHookAfter('SeoNeo::getJsonLd', function($event) {
$page = $event->arguments(0);
if ($page->template->name === 'homepage') {
$graph = $event->return;
$graph['@graph'] = array_filter(
$graph['@graph'],
fn($node) => $node['@type'] !== 'BreadcrumbList'
);
$event->return = $graph;
}
});
Disabling JSON-LD
The Emit JSON-LD checkbox in module config is the master switch. Untick it and no <script type="application/ld+json"> block is emitted on any page. For per-page or per-template suppression, hook ___renderJsonLd($page) and return an empty string in the cases you want to skip.
See also
- Configuration — publisher type, article templates, and BreadcrumbList settings.
- Hooks & Customisation — extending or overriding the graph with
getJsonLdandrenderJsonLdhooks. - Multilingual — per-language Organization name and description maps.
Last updated