Complex landing page

Copy Markdown View Source

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"> string

In a Phoenix HEEx template (needs :phoenix_live_view):

<SchemaOrg.HTML.json_ld data={@nodes} />