Docs SEO NEO Version 1.x Structured Data (JSON-LD)

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 @graph generator 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 on renderHead is 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, with name, url, optional logo, description, and sameAs links. Choose Person for 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 publisher reference back to the Organization and the default-language inLanguage.
  • WebPage — the current page, with name, description, url, inLanguage, isPartOf, dateModified, datePublished, and primaryImageOfPage when an OG image resolves.
  • Article — added on pages whose template appears in Article templates in module config (e.g. blog_post,journal_post). Includes headline, description, image, datePublished, dateModified, resolved author, publisher reference, and inLanguage.
  • Person — added on pages whose template appears in Person templates (defaults to user, so PW User pages used as author bios are recognised). Includes name, description, image, and url.
  • 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-LDon by default. Disable for minified production output.
  • Publisher type — the @type for the Organization node. Includes Person for 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=value maps for multilingual Organization nodes. The @id URIs 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.url on the publisher node and as publisher.logo on Article nodes. Required by Google for certain rich-result eligibility.
  • Publisher sameAs URLs — one URL per line. Used as the publisher's sameAs array — 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_author field 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 getJsonLd and renderJsonLd hooks.
  • Multilingual — per-language Organization name and description maps.

Last updated