<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Brenner Cruvinel - schema</title>
    <subtitle>AI researcher and product designer building at the edge of computer science and mental health.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://brennercruvinel.blog/en/tags/schema/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://brennercruvinel.blog"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-05-20T00:00:00+00:00</updated>
    <id>https://brennercruvinel.blog/en/tags/schema/atom.xml</id>
    <entry xml:lang="en">
        <title>GEO, schema and JSON-LD to get cited by LLM engines</title>
        <published>2026-05-20T00:00:00+00:00</published>
        <updated>2026-05-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            Brenner Cruvinel
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://brennercruvinel.blog/en/blog/geo-schema-json-ld/"/>
        <id>https://brennercruvinel.blog/en/blog/geo-schema-json-ld/</id>
        
        <content type="html" xml:base="https://brennercruvinel.blog/en/blog/geo-schema-json-ld/">&lt;p&gt;I wrote this guide in May 2026, after putting what I knew about schema next to what the GEO (generative engine optimization) papers were measuring. On June 19 I applied it to this blog in three changes: the single @graph, the BreadcrumbList, and a bundle with speakable, a TL;DR block and a robots.txt that allows AI crawlers. So the example in this post is no longer a made-up &lt;code&gt;example.com&lt;/code&gt;. It is the JSON-LD this page serves right now.&lt;/p&gt;
&lt;p&gt;The idea that ties it all together: clean JSON-LD is no longer just crawler food. It is what generative engines read to decide who gets credit for an answer, and it is the format NLWeb consumes and returns.&lt;/p&gt;
&lt;h2 id=&quot;graph-and-id&quot;&gt;@graph and @id&lt;/h2&gt;
&lt;p&gt;The difference that matters is between loose JSON-LD blocks and a single connected graph. Each entity is declared once, with a stable &lt;code&gt;@id&lt;/code&gt;, and everything else points to it with &lt;code&gt;{ &quot;@id&quot;: &quot;...&quot; }&lt;/code&gt;. One &lt;code&gt;@graph&lt;/code&gt; block per page, not several scripts fighting each other.&lt;/p&gt;
&lt;p&gt;The anchoring rule I follow, in line with the &lt;a rel=&quot;external&quot; href=&quot;https://github.com/w3c/json-ld-bp&quot;&gt;W3C JSON-LD best practices&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Site-wide entities anchor at the root with a fragment: &lt;code&gt;/#organization&lt;/code&gt;, &lt;code&gt;/#website&lt;/code&gt;, &lt;code&gt;/#person&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Page entities anchor at the page URL: &lt;code&gt;#article&lt;/code&gt;, &lt;code&gt;#webpage&lt;/code&gt;, &lt;code&gt;#breadcrumb&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The page entity references site entities by &lt;code&gt;@id&lt;/code&gt; and never copies the object&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I declare site entities before page entities. The W3C does not require it. It is hygiene: a linear parser resolves the nodes in the right order.&lt;/p&gt;
&lt;p&gt;The three bugs that show up most when auditing schema, all of which go away with disciplined &lt;code&gt;@id&lt;/code&gt; values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@id&lt;/code&gt; collision: two nodes with the same &lt;code&gt;@id&lt;/code&gt; get merged by the parser into one, with unpredictable results&lt;/li&gt;
&lt;li&gt;Orphan reference: an &lt;code&gt;@id&lt;/code&gt; that is referenced but does not exist in any node of the graph&lt;/li&gt;
&lt;li&gt;Plugin duplication: Yoast and RankMath each injecting their own Organization, with different data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The third one is the strongest argument for generating schema in one place. Here that place is &lt;code&gt;templates/partials/schema.html&lt;/code&gt;, a Zola partial that builds the whole graph from &lt;code&gt;config.toml&lt;/code&gt; and the post front matter.&lt;/p&gt;
&lt;p&gt;The other rule is triple agreement: visible text, semantic HTML and JSON-LD saying the same thing. The &lt;a rel=&quot;external&quot; href=&quot;https://arxiv.org/abs/2509.10762&quot;&gt;GEO-16 paper&lt;/a&gt; audited 1,100 URLs cited by Brave Summary, Google AI Overviews and Perplexity, and the pillars most correlated with citation were metadata and freshness (r = 0.68), semantic HTML (r = 0.65) and structured data (r = 0.63). In other words, semantic HTML weighed as much as schema. Schema that describes something not on the page gets ignored, at best.&lt;/p&gt;
&lt;h2 id=&quot;the-graph-this-blog-serves&quot;&gt;The @graph this blog serves&lt;/h2&gt;
&lt;p&gt;This is the graph for the &lt;a href=&quot;https://brennercruvinel.blog/en/blog/meu-airpods-max-voltou-do-congelador/&quot;&gt;AirPods Max&lt;/a&gt; post, without the long fields (description, image, word count):&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#657B83, #839496); background-color: light-dark(#FDF6E3, #002B36);&quot; &gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;  &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@context&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://schema.org&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;  &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@graph&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@type&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;Organization&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@id&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://brennercruvinel.blog/#organization&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;Brenner Cruvinel&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://brennercruvinel.blog/&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;sameAs&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://github.com/brennercruvinel&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;knowsAbout&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;artificial intelligence&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;mental health tech&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;product design&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@type&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;WebSite&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@id&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://brennercruvinel.blog/#website&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://brennercruvinel.blog/&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;inLanguage&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;pt-BR&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;publisher&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@id&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://brennercruvinel.blog/#organization&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@type&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;Person&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@id&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://brennercruvinel.blog/#person&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;Brenner Cruvinel&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;sameAs&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://github.com/brennercruvinel&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@type&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;BlogPosting&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@id&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://brennercruvinel.blog/blog/meu-airpods-max-voltou-do-congelador/#article&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;isPartOf&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@id&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://brennercruvinel.blog/#website&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;headline&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;Meu AirPods Max morreu e voltou do congelador&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;datePublished&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;2026-10-10T00:00:00+0000&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;author&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@id&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://brennercruvinel.blog/#person&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;publisher&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@id&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://brennercruvinel.blog/#organization&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;speakable&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;        &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@type&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;SpeakableSpecification&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;        &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;cssSelector&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;.tldr&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@type&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;BreadcrumbList&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@id&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://brennercruvinel.blog/blog/meu-airpods-max-voltou-do-congelador/#breadcrumb&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;itemListElement&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        {&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@type&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;ListItem&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;position&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D33682, #D33682);&quot;&gt; 1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;Home&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;item&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://brennercruvinel.blog/&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        {&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;@type&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;ListItem&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;position&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D33682, #D33682);&quot;&gt; 2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;Writings&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;item&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://brennercruvinel.blog/blog/&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Read it as an entity database, not as a list of tags. Organization, WebSite and Person exist once. The BlogPosting points to them. &lt;code&gt;speakable&lt;/code&gt; says which part of the HTML is good for direct extraction, here the TL;DR block at the top of every post.&lt;/p&gt;
&lt;p&gt;The template can also generate a Dataset node and a DefinedTermSet when a post&#39;s front matter declares &lt;code&gt;extra.dataset&lt;/code&gt; or &lt;code&gt;extra.terms&lt;/code&gt;. No post uses that yet.&lt;/p&gt;
&lt;p&gt;What is still missing here, and is commented out in &lt;code&gt;config.toml&lt;/code&gt;: the &lt;code&gt;sameAs&lt;/code&gt; to Wikidata (I do not have an entry there) and the SearchAction (the blog has no URL-based search).&lt;/p&gt;
&lt;h2 id=&quot;which-types-to-use&quot;&gt;Which types to use&lt;/h2&gt;
&lt;p&gt;The base shows up on almost every site: Organization or LocalBusiness for the brand, WebSite, WebPage per page, Person for authors and BreadcrumbList. On top of that:&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Activity&lt;/th&gt;&lt;th&gt;Main types&lt;/th&gt;&lt;th&gt;Worth adding&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Blog, editorial site&lt;/td&gt;&lt;td&gt;Article, BlogPosting&lt;/td&gt;&lt;td&gt;SpeakableSpecification, Claim&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;E-commerce&lt;/td&gt;&lt;td&gt;Product, Offer, Review&lt;/td&gt;&lt;td&gt;Dataset for the catalog&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Local business&lt;/td&gt;&lt;td&gt;LocalBusiness&lt;/td&gt;&lt;td&gt;DefinedTerm for services&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;SaaS, software&lt;/td&gt;&lt;td&gt;SoftwareApplication, Service&lt;/td&gt;&lt;td&gt;Dataset for the changelog&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Documentation&lt;/td&gt;&lt;td&gt;TechArticle, HowTo&lt;/td&gt;&lt;td&gt;DefinedTermSet&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Research&lt;/td&gt;&lt;td&gt;Article, Person&lt;/td&gt;&lt;td&gt;Claim, ClaimReview&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The types I think are underused:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DefinedTerm and DefinedTermSet register your own vocabulary that does not exist in Wikidata&lt;/li&gt;
&lt;li&gt;Dataset turns an article&#39;s metrics table into its own node, with &lt;code&gt;license&lt;/code&gt; and &lt;code&gt;temporalCoverage&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Claim and ClaimReview mark statements and fact checks, useful for research content&lt;/li&gt;
&lt;li&gt;SoftwareSourceCode marks content as a technical reference&lt;/li&gt;
&lt;li&gt;VideoObject ties the video to the channel entity&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;where-schema-lives-on-each-platform&quot;&gt;Where schema lives on each platform&lt;/h2&gt;
&lt;p&gt;The principle does not change: the &lt;code&gt;@graph&lt;/code&gt; is a data artifact, not a UI artifact. It is serialized once from the source of truth and has to be where the consumer looks before any JavaScript runs. On the web, the consumer is a crawler reading the initial HTML, so JSON-LD comes from the server, never from a &lt;code&gt;useEffect&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The source of truth is the domain modeled as entities, with types validated in the backend language: Pydantic in Python, Zod or schema-dts in TypeScript, serde in Rust, Codable in Swift, kotlinx.serialization in Kotlin. Each entity has a stable internal identifier that becomes the &lt;code&gt;@id&lt;/code&gt;, and a single module generates the JSON-LD. That module is what kills duplication.&lt;/p&gt;
&lt;p&gt;In Next.js with the App Router, schema goes in a Server Component. &lt;a rel=&quot;external&quot; href=&quot;https://github.com/google/schema-dts&quot;&gt;schema-dts&lt;/a&gt; is Google&#39;s types package, types only, with no runtime cost, and it had 3,127,725 downloads in the week of October 5 to 11, 2026, according to the npm API. Site entities go in the root layout; page entities in the page component. For a blog, SSG or ISR beats plain SSR, because the schema comes out at build time.&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#657B83, #839496); background-color: light-dark(#FDF6E3, #002B36);&quot; &gt;&lt;code data-lang=&quot;tsx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#93A1A1, #586E75);font-style: italic;&quot;&gt;//&lt;/span&gt;&lt;span style=&quot;color: light-dark(#93A1A1, #586E75);font-style: italic;&quot;&gt; app/blog/[slug]/page.tsx, Server Component&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; type&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt; Graph&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt; WithContext&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;schema-dts&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color: light-dark(#586E75, #93A1A1);font-weight: bold;&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color: light-dark(#586E75, #93A1A1);font-weight: bold;&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt; ArticlePage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; params&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#586E75, #93A1A1);font-weight: bold;&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt; post&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt; getPost&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt;params&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt;slug&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#586E75, #93A1A1);font-weight: bold;&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt; schema&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt; WithContext&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt;Graph&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;    &amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;@context&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://schema.org&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;    &amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;@graph&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;      &amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;@type&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;Article&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;      &amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;@id&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; `&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://example.com/blog/&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt;params&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt;slug&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;#article&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      headline&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt; post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt;title&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      author&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;@id&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://example.com/#person&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      publisher&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;@id&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;https://example.com/#organization&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      datePublished&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt; post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt;publishedAt&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      dateModified&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt; post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt;updatedAt&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#93A1A1, #586E75);&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color: light-dark(#93A1A1, #93A1A1);&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;application/ld+json&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#93A1A1, #93A1A1);&quot;&gt;      dangerouslySetInnerHTML&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: light-dark(#DC322F, #DC322F);&quot;&gt;{&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; __html&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt; JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt;stringify&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt;schema&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span style=&quot;color: light-dark(#DC322F, #DC322F);&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: light-dark(#93A1A1, #586E75);&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In a pure SPA, with Vite or CRA, the problem has no reliable solution: most AI crawlers do not run JavaScript. Static content goes in &lt;code&gt;index.html&lt;/code&gt;; dynamic content needs prerendering or a meta-framework with SSR. Rails, Django, Laravel and Go are the easy case: the server template prints the script.&lt;/p&gt;
&lt;p&gt;Rust on the server (axum, actix, Leptos with SSR) is the cleanest case, because the source of truth and the serializer are the same typed language:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#657B83, #839496); background-color: light-dark(#FDF6E3, #002B36);&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;derive&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;serde&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt;Serialize&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#586E75, #93A1A1);font-weight: bold;&quot;&gt;struct&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt; Article&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    #&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;serde&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;rename &lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;@type&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt; kind&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt; str&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    #&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;serde&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;rename &lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;@id&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt; String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt;    headline&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt; str&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt;    author&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt; IdRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: light-dark(#93A1A1, #586E75);font-style: italic;&quot;&gt;     //&lt;/span&gt;&lt;span style=&quot;color: light-dark(#93A1A1, #586E75);font-style: italic;&quot;&gt; { &amp;quot;@id&amp;quot;: &amp;quot;...&amp;quot; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt;    publisher&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt; IdRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    #&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;serde&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;rename &lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;datePublished&lt;/span&gt;&lt;span style=&quot;color: light-dark(#2AA198, #2AA198);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span style=&quot;color: light-dark(#268BD2, #268BD2);&quot;&gt; published&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#859900, #859900);&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CB4B16, #CB4B16);&quot;&gt; str&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#93A1A1, #586E75);font-style: italic;&quot;&gt;//&lt;/span&gt;&lt;span style=&quot;color: light-dark(#93A1A1, #586E75);font-style: italic;&quot;&gt; serde_json::to_string(&amp;amp;graph) becomes the body of &amp;lt;script type=&amp;quot;application/ld+json&amp;quot;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rust compiled to WebAssembly in the browser falls into the same hole as the SPA. If the wasm builds the page on the client, the crawler sees nothing. The article &lt;a rel=&quot;external&quot; href=&quot;https://www.gtechme.com/insights/webassembly-seo-future-challenges/&quot;&gt;The Future of SEO with WebAssembly&lt;/a&gt;, from the agency GTECH, argues the same: server-rendered HTML with text, headings and links on first load, and wasm only on top, for the heavy work.&lt;/p&gt;
&lt;p&gt;In a native app, Swift or Kotlin, there is no crawler reading the screen. Putting JSON-LD in a SwiftUI view gives you no visibility at all. What makes the content discoverable is the backend: it serves the same graph as HTML to crawlers and through a structured endpoint to agents, and the app consumes that backend. Firebase App Indexing, which indexed app content, is on Firebase&#39;s list of deprecated libraries; Android App Links and Universal Links are still useful, but they are routing, not discovery.&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Platform&lt;/th&gt;&lt;th&gt;Where schema lives&lt;/th&gt;&lt;th&gt;How it gets there&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Next.js, Nuxt, SvelteKit, Astro, Zola&lt;/td&gt;&lt;td&gt;Server component, SSR or SSG&lt;/td&gt;&lt;td&gt;Script in the initial HTML&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Rails, Django, Laravel, Go&lt;/td&gt;&lt;td&gt;Server template&lt;/td&gt;&lt;td&gt;Script in the initial HTML&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Pure SPA&lt;/td&gt;&lt;td&gt;&lt;code&gt;index.html&lt;/code&gt; or prerender&lt;/td&gt;&lt;td&gt;Served HTML&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Rust on the server&lt;/td&gt;&lt;td&gt;serde in the handler&lt;/td&gt;&lt;td&gt;Script in the initial HTML&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Rust/wasm on the client&lt;/td&gt;&lt;td&gt;Prerender or SSR&lt;/td&gt;&lt;td&gt;Never client-only&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swift, Kotlin, desktop&lt;/td&gt;&lt;td&gt;Backend the app consumes&lt;/td&gt;&lt;td&gt;Endpoint, &lt;code&gt;/ask&lt;/code&gt; and &lt;code&gt;/mcp&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Schema breaks silently. A structural change that corrupts an &lt;code&gt;@id&lt;/code&gt; only shows up weeks later, when the citations disappear. That is why it pays to validate before every deploy: the &lt;a rel=&quot;external&quot; href=&quot;https://validator.schema.org/&quot;&gt;Schema Markup Validator&lt;/a&gt; checks against the vocabulary, the &lt;a rel=&quot;external&quot; href=&quot;https://search.google.com/test/rich-results&quot;&gt;Rich Results Test&lt;/a&gt; checks the rich results that still exist, and schema-dts catches errors at compile time.&lt;/p&gt;
&lt;h2 id=&quot;faqpage-after-may-7-2026&quot;&gt;FAQPage after May 7, 2026&lt;/h2&gt;
&lt;p&gt;Google removed FAQ rich results in stages, announced in a notice in the documentation, with no blog post. On May 7, 2026 they stopped showing in search. On June 15 Google removed the feature&#39;s documentation, as the &lt;a rel=&quot;external&quot; href=&quot;https://developers.google.com/search/updates&quot;&gt;Search Central changelog&lt;/a&gt; records. The same announcement planned to drop support in the Search Console API in August.&lt;/p&gt;
&lt;p&gt;What remains: FAQPage is still a valid schema.org type. Google turned off the display, not the markup, and other crawlers keep reading it. What always did the work was the content in question-and-answer form.&lt;/p&gt;
&lt;p&gt;One number helps explain why. Ahrefs analyzed 863,000 queries and 4 million URLs cited in AI Overviews, in a study published in March 2026 and &lt;a rel=&quot;external&quot; href=&quot;https://www.searchenginejournal.com/google-ai-overview-citations-from-top-ranking-pages-drop-sharply/568637/&quot;&gt;summarized by Search Engine Journal&lt;/a&gt;: only 38% of the cited pages were in the top 10 for the same query. In the July 2025 version it was 76%, but Ahrefs itself warns that it improved citation detection between versions. Even with that caveat, what earns citations is not only what earns rankings.&lt;/p&gt;
&lt;p&gt;My decision: I keep FAQPage where the question and the answer are real and visible on the page. I remove it only where the section existed to decorate the SERP.&lt;/p&gt;
&lt;h2 id=&quot;nlweb-and-mcp&quot;&gt;NLWeb and MCP&lt;/h2&gt;
&lt;p&gt;NLWeb was &lt;a rel=&quot;external&quot; href=&quot;https://news.microsoft.com/source/features/company-news/introducing-nlweb-bringing-conversational-interfaces-directly-to-the-web/&quot;&gt;announced by Microsoft at Build 2025&lt;/a&gt; and was conceived by R.V. Guha, the same person behind RSS, RDF and Schema.org. Among the first partners named are Shopify, Snowflake, O&#39;Reilly, Tripadvisor, Eventbrite and Hearst. It turns a site into an MCP server with two endpoints:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/ask&lt;/code&gt; takes a natural language question and returns JSON in schema.org&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/mcp&lt;/code&gt; exposes the same to agents, over the Model Context Protocol&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The arguments are the same for both; only the response format changes. The reference implementation keeps no state: the conversation context comes back with each call, in the &lt;code&gt;prev&lt;/code&gt; parameter or as an already decontextualized query.&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#657B83, #839496); background-color: light-dark(#FDF6E3, #002B36);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;POST https://example.com/ask&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Content-Type: application/json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{ &amp;quot;query&amp;quot;: &amp;quot;who wrote about GEO on this site?&amp;quot;, &amp;quot;site&amp;quot;: &amp;quot;example.com&amp;quot; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The implication: a well-built &lt;code&gt;@graph&lt;/code&gt; becomes a conversational API without rewriting the backend. It works best on sites already structured as lists of items, recipes, events, products. The repository moved from &lt;code&gt;microsoft/NLWeb&lt;/code&gt; to &lt;a rel=&quot;external&quot; href=&quot;https://github.com/nlweb-ai/NLWeb&quot;&gt;nlweb-ai/NLWeb&lt;/a&gt; in July 2025. It is written in Python and now ships modules such as AskAgent, AgentFinder and DataFinder, with Postgres among the supported vector stores.&lt;/p&gt;
&lt;p&gt;For a native app with no web version, exposing &lt;code&gt;/ask&lt;/code&gt; and &lt;code&gt;/mcp&lt;/code&gt; over the data is the way to be found by agents.&lt;/p&gt;
&lt;h2 id=&quot;the-robots-txt-and-the-llms-txt&quot;&gt;The robots.txt and the llms.txt&lt;/h2&gt;
&lt;p&gt;If the goal is to be cited by AI, LLM user agents stay allowed in robots.txt. Blocking is a strategic decision, as with paid content, knowing it takes the site out of that channel. This blog&#39;s robots.txt allows, among others:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#657B83, #839496); background-color: light-dark(#FDF6E3, #002B36);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;User-agent: GPTBot&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Allow: /&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;User-agent: OAI-SearchBot&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Allow: /&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;User-agent: PerplexityBot&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Allow: /&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;User-agent: ClaudeBot&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Allow: /&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;User-agent: Google-Extended&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Allow: /&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;User-agent: CCBot&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Allow: /&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GPTBot and OAI-SearchBot belong to OpenAI, PerplexityBot to Perplexity, ClaudeBot to Anthropic. Google-Extended controls use in Gemini and CCBot is Common Crawl.&lt;/p&gt;
&lt;p&gt;The llms.txt file is a markdown index of the important URLs. It blocks nothing and ranks nothing. Gary Illyes said in July 2025 that Google does not use it and does not plan to, and John Mueller compared the file to the keywords meta tag. On June 15, 2026 Google put this in its &lt;a rel=&quot;external&quot; href=&quot;https://developers.google.com/search/docs/fundamentals/ai-optimization-guide#mythbusting&quot;&gt;own AI optimization guide&lt;/a&gt;: the file is not needed for search and neither helps nor hurts rankings. This blog has no llms.txt, and I do not plan to prioritize one.&lt;/p&gt;
&lt;h2 id=&quot;four-layers-for-an-llm-ready-engine&quot;&gt;Four layers for an LLM-ready engine&lt;/h2&gt;
&lt;p&gt;When schema stops being just SEO and becomes data for an LLM, I think in four layers.&lt;/p&gt;
&lt;p&gt;The first is content modeled as entities: types, fields, what null means, one stable identifier per entity, with source, update date and language recorded. It is the internal graph, independent of schema.org.&lt;/p&gt;
&lt;p&gt;The second is public exposure: the internal graph translated into schema.org, injected into the initial HTML, with triple agreement guaranteed.&lt;/p&gt;
&lt;p&gt;The third is RAG. Each chunk in the vector index carries the text, the entity&#39;s &lt;code&gt;@id&lt;/code&gt;, the type, the date and the language. The same identifier vocabulary everywhere lets the LLM stitch together a coherent answer across the JSON-LD, the documents and the API.&lt;/p&gt;
&lt;p&gt;The fourth is the application: tools with input and output schemas over the entities, the LLM in structured output, and the front end treating the answer as a view of the graph, always showing the source.&lt;/p&gt;
&lt;p&gt;What ties the four together is the same entity model in the JSON-LD, the backend, the tools and the front-end types. And it is worth logging when the LLM cites an entity by &lt;code&gt;@id&lt;/code&gt;, to compare with referrals from AI Overviews and ChatGPT after each schema change.&lt;/p&gt;
&lt;h2 id=&quot;what-the-papers-say-helps&quot;&gt;What the papers say helps&lt;/h2&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https://arxiv.org/abs/2311.09735&quot;&gt;original GEO paper&lt;/a&gt;, by Aggarwal and others, published at KDD 2024, tested content rewrites and measured visibility in the answers. The three methods that worked best were citing sources, adding direct quotations and adding statistics, with a relative gain of 30% to 40% on the position-adjusted word count metric. Citing other sources raises the chance that you get cited.&lt;/p&gt;
&lt;p&gt;The rest is the basics done well: answer at the top, short paragraphs, descriptive headings, a single H1, real &lt;code&gt;datePublished&lt;/code&gt; and &lt;code&gt;dateModified&lt;/code&gt;, and primary sources with links.&lt;/p&gt;
&lt;h2 id=&quot;checklist&quot;&gt;Checklist&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;A single &lt;code&gt;@graph&lt;/code&gt; block per page&lt;/li&gt;
&lt;li&gt;Organization, WebSite and Person declared once, anchored at the root with &lt;code&gt;#&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Page entities anchored at the URL, referencing site entities by &lt;code&gt;@id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Site entities before page entities&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sameAs&lt;/code&gt; to Wikidata on Organization and Person, when an entry exists&lt;/li&gt;
&lt;li&gt;&lt;code&gt;knowsAbout&lt;/code&gt; with the real topics of authority&lt;/li&gt;
&lt;li&gt;SpeakableSpecification pointing to the summary and the headings&lt;/li&gt;
&lt;li&gt;Dataset when there is a data table, with &lt;code&gt;license&lt;/code&gt; and &lt;code&gt;temporalCoverage&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;DefinedTerm for your own vocabulary&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dateModified&lt;/code&gt; updated on every real edit&lt;/li&gt;
&lt;li&gt;JSON-LD in the initial HTML; in a native app, through an endpoint&lt;/li&gt;
&lt;li&gt;Schema typed in the backend language&lt;/li&gt;
&lt;li&gt;Triple agreement between text, semantic HTML and JSON-LD&lt;/li&gt;
&lt;li&gt;AI user agents allowed in robots.txt&lt;/li&gt;
&lt;li&gt;Validation in the Schema Markup Validator and in CI&lt;/li&gt;
&lt;li&gt;NLWeb &lt;code&gt;/ask&lt;/code&gt; and &lt;code&gt;/mcp&lt;/code&gt; once the graph is stable&lt;/li&gt;
&lt;li&gt;llms.txt last, if at all&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Pranjal Aggarwal et al., &lt;a rel=&quot;external&quot; href=&quot;https://doi.org/10.1145/3637528.3671900&quot;&gt;GEO: Generative Engine Optimization&lt;/a&gt;, KDD 2024&lt;/li&gt;
&lt;li&gt;Arlen Kumar and Leanid Palkhouski, &lt;a rel=&quot;external&quot; href=&quot;https://arxiv.org/abs/2509.10762&quot;&gt;AI Answer Engine Citation Behavior: An Empirical Analysis of the GEO-16 Framework&lt;/a&gt;, 2025&lt;/li&gt;
&lt;li&gt;Mahe Chen et al., &lt;a rel=&quot;external&quot; href=&quot;https://arxiv.org/abs/2509.08919&quot;&gt;Generative Engine Optimization: How to Dominate AI Search&lt;/a&gt;, 2025&lt;/li&gt;
&lt;li&gt;Junwei Yu et al., &lt;a rel=&quot;external&quot; href=&quot;https://arxiv.org/abs/2603.29979&quot;&gt;Structural Feature Engineering for Generative Engine Optimization&lt;/a&gt;, 2026, which measured 17.3% more citations by changing only the structure of the content&lt;/li&gt;
&lt;li&gt;W3C, &lt;a rel=&quot;external&quot; href=&quot;https://github.com/w3c/json-ld-bp&quot;&gt;JSON-LD Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Google, &lt;a rel=&quot;external&quot; href=&quot;https://github.com/google/schema-dts&quot;&gt;schema-dts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/nlweb-ai/NLWeb&quot;&gt;nlweb-ai/NLWeb&lt;/a&gt;, the NLWeb reference implementation&lt;/li&gt;
&lt;/ol&gt;
</content>
        
    </entry>
</feed>
