1
Manual
Hand-written JSON-LD per page. Accurate on day one, drifts as content changes, and does not scale past a few pages.
Each page maps one Google rich result type to a JavaScript conditional-logic framework so your implementation can adapt to real content states instead of relying on brittle static JSON templates.
Most structured data starts as JSON-LD written by hand for one page. That works for a handful of pages and breaks down at scale: every new page is another snippet to write, and every content change is another snippet to update.
Templates solve scale. The CMS fills one JSON-LD template with fields from each content object, so a single template covers every event, product or person. But content objects are rarely complete. When a field is missing, the template still prints the property, now empty, and a variable printed without quotes can break the JSON so that nothing in the block parses.
A conditional-logic framework builds the markup from the data that actually exists. Each property, and each nested object, is added only when the content object has the field that feeds it, and the whole item is skipped when Google's required properties are missing.
1
Manual
Hand-written JSON-LD per page. Accurate on day one, drifts as content changes, and does not scale past a few pages.
2
Templated
One JSON template per content type, filled by the CMS. Scales, but prints empty or broken properties whenever a field is missing.
3
Conditional logic
Code that adds each property and nested object only when its data exists, and checks Google's rules before emitting anything.
One Person content model, two content objects. Jane Doe has a name only. John Doe has a profile URL and a job with a title and a company.
{
"@context": "https://schema.org",
"@type": "Person",
"name": "{{ person.name }}",
"url": "{{ person.url }}",
"jobTitle": "{{ person.job.title }}",
"worksFor": {
"@type": "Organization",
"name": "{{ person.job.company }}"
}
}
{
"@context": "https://schema.org",
"@type": "Person",
"name": "Jane Doe",
"url": "",
"jobTitle": "",
"worksFor": {
"@type": "Organization",
"name": ""
}
}
function buildPersonSchema(person) {
if (!person.name) return null;
const schema = {
"@context": "https://schema.org",
"@type": "Person",
"name": person.name
};
if (person.url) schema.url = person.url;
if (person.job) {
const worksFor = { "@type": "Organization" };
if (person.job.company) worksFor.name = person.job.company;
if (person.job.companyUrl) worksFor.url = person.job.companyUrl;
if (person.job.title) schema.jobTitle = person.job.title;
if (worksFor.name) schema.worksFor = worksFor;
}
return schema;
}
{
"@context": "https://schema.org",
"@type": "Person",
"name": "Jane Doe"
}
{
"@context": "https://schema.org",
"@type": "Person",
"name": "John Doe",
"url": "https://www.example.com/john-doe",
"jobTitle": "Head of SEO",
"worksFor": {
"@type": "Organization",
"name": "Example Co",
"url": "https://www.example.com"
}
}
The template gives Jane three empty properties (url, jobTitle and the nested company name), which validators flag and search engines ignore at best. The conditional version gives her a clean Person with only her name, and gives John a url, a jobTitle and a nested Organization because his data supports them.
Features Google has retired, such as HowTo and the sitelinks search box, stay in the library with a dated notice. No rich result appears for them any more, but the markup can still help search engines and AI assistants understand the page.
40 frameworks, one per Google rich result feature, each with JavaScript and Python builders and a validated output example. The 8 features Google has retired are marked on their cards.
A real page uses several of these frameworks at once. The page types show the whole connected graph for the most common pages, each from one builder, linked by @id and validated. See how the page types fit together.
Implement robust rich result markup using logic-driven frameworks that reflect real page state, not static one-off templates.