A real landing page describes several independent things at once — the
organization behind the site, the site itself, a breadcrumb trail, an FAQ. The
idiomatic way to express this is a single JSON-LD document with a top-level
@graph array holding each node.
Pass a list of structs to to_json_ld/1 (or to_map/1) and they are
wrapped in @graph automatically:
nodes = [
%SchemaOrg.Organization{
name: "Acme Inc.",
url: "https://example.com",
logo: %SchemaOrg.ImageObject{url: "https://example.com/logo.png"},
same_as: ["https://twitter.com/acme", "https://linkedin.com/company/acme"],
contact_point: %SchemaOrg.ContactPoint{
telephone: "+1-401-555-1212",
contact_type: "customer service"
}
},
%SchemaOrg.WebSite{name: "Acme", url: "https://example.com"},
%SchemaOrg.BreadcrumbList{
item_list_element: [
%SchemaOrg.ListItem{position: 1, name: "Home", item: "https://example.com"},
%SchemaOrg.ListItem{position: 2, name: "Pricing", item: "https://example.com/pricing"}
]
},
%SchemaOrg.FAQPage{
main_entity: [
%SchemaOrg.Question{
name: "Is there a free trial?",
accepted_answer: %SchemaOrg.Answer{text: "Yes, 14 days."}
}
]
}
]
SchemaOrg.to_json_ld(nodes)produces:
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "Organization",
"name": "Acme Inc.",
"url": "https://example.com",
"logo": { "@type": "ImageObject", "url": "https://example.com/logo.png" },
"sameAs": ["https://twitter.com/acme", "https://linkedin.com/company/acme"],
"contactPoint": {
"@type": "ContactPoint",
"telephone": "+1-401-555-1212",
"contactType": "customer service"
}
},
{ "@type": "WebSite", "name": "Acme", "url": "https://example.com" },
{
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com" },
{ "@type": "ListItem", "position": 2, "name": "Pricing", "item": "https://example.com/pricing" }
]
},
{
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Is there a free trial?",
"acceptedAnswer": { "@type": "Answer", "text": "Yes, 14 days." }
}
]
}
]
}The top-level @context is emitted once and applies to every node in the
graph. The nodes here are inlined; cross-referencing shared entities by
@id (so the Article publisher points at the Organization node instead of
repeating it) is not yet supported — see the README roadmap.
Embed it in a page
One <script> tag carries the whole graph:
SchemaOrg.to_script_tag(nodes) # HTML-safe <script type="application/ld+json"> stringIn a Phoenix HEEx template (needs :phoenix_live_view):
<SchemaOrg.HTML.json_ld data={@nodes} />