All templates
SEO · AEO · GEO~20 minCW-2

JSON-LD schemas — the AEO foundation with copy-paste blocks

Eight ready-to-use Schema.org structures for every page type, with Next.js render patterns and validation steps.

Adding structured data is the single highest-leverage AEO move you can make. AI engines (Perplexity, ChatGPT search, Google AI Overviews) preferentially cite pages where they can parse 'this is a Service from this Organization at this price' without ambiguity. This guide gives you eight ready-to-use JSON-LD blocks (Person, Organization, Service, FAQ, Article, BreadcrumbList, ContactPoint, ItemList) with Next.js render patterns. Drop in, validate, ship.

The Next.js render pattern

Every JSON-LD block follows the same pattern: define the object as a plain JS const, render with a <script> tag using dangerouslySetInnerHTML. Place inside your page's JSX root or layout — either works.

const jsonLd = {
  "@context": "https://schema.org",
  "@type": "Person",
  name: "Your Name",
  // ...other fields
};

export default function Page() {
  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      <main>...your page content...</main>
    </>
  );
}

Multiple schemas per page

You can have multiple JSON-LD blocks on one page (e.g. BreadcrumbList + Article + FAQPage). Each goes in its own <script> tag.

Eight ready-to-use schemas

  1. 1.Person (for personal sites)

    Use on the homepage of personal/freelance/consultant sites.

    {
      "@context": "https://schema.org",
      "@type": "Person",
      "name": "Your Name",
      "url": "https://yourdomain.com",
      "image": "https://yourdomain.com/avatar.jpg",
      "jobTitle": "Product strategist",
      "worksFor": {
        "@type": "Organization",
        "name": "Your Business"
      },
      "description": "Freelance product strategist working with B2B SaaS founders in Singapore.",
      "address": {
        "@type": "PostalAddress",
        "addressLocality": "Singapore",
        "addressCountry": "SG"
      },
      "sameAs": [
        "https://linkedin.com/in/yourname",
        "https://github.com/yourname",
        "https://x.com/yourname"
      ]
    }

    sameAs is the AEO superpower

    The sameAs array tells AI engines 'this Person is also at these URLs'. It connects your site to your LinkedIn/GitHub/X — which lets AI engines verify your identity and cite you confidently.

  2. 2.Organization (for business sites)

    Use on the homepage of business sites and on /about.

    {
      "@context": "https://schema.org",
      "@type": "Organization",
      "name": "Your Business",
      "url": "https://yourdomain.com",
      "logo": "https://yourdomain.com/logo.png",
      "description": "What your business does, in one sentence.",
      "founder": {
        "@type": "Person",
        "name": "Your Name"
      },
      "foundingDate": "2024",
      "numberOfEmployees": {
        "@type": "QuantitativeValue",
        "value": "5"
      },
      "address": {
        "@type": "PostalAddress",
        "streetAddress": "123 Example Street",
        "addressLocality": "Singapore",
        "postalCode": "123456",
        "addressCountry": "SG"
      },
      "contactPoint": {
        "@type": "ContactPoint",
        "contactType": "customer service",
        "email": "hello@yourdomain.com",
        "telephone": "+65-1234-5678",
        "areaServed": "SG",
        "availableLanguage": "English"
      },
      "sameAs": [
        "https://linkedin.com/company/yourbusiness",
        "https://x.com/yourbusiness"
      ]
    }
  3. 3.Service (for each thing you sell)

    Use on /services pages or individual service detail pages.

    {
      "@context": "https://schema.org",
      "@type": "Service",
      "name": "Positioning sprint",
      "description": "4-week positioning sprint for B2B SaaS founders past US$100k ARR. Produces messaging your sales team uses verbatim.",
      "provider": {
        "@type": "Organization",
        "name": "Your Business",
        "url": "https://yourdomain.com"
      },
      "areaServed": {
        "@type": "Country",
        "name": "Singapore"
      },
      "serviceType": "Marketing strategy",
      "offers": {
        "@type": "Offer",
        "price": "8000",
        "priceCurrency": "SGD",
        "availability": "https://schema.org/InStock"
      }
    }

    Even indicative pricing helps

    If you don't have a fixed price, use a typical or starting price. 'priceSpecification' with minPrice/maxPrice is also valid. Some pricing data > no pricing data for AEO purposes.

  4. 4.FAQPage (Google rich result + AEO)

    Use on any page with a real FAQ section. Don't fake the FAQ — Google penalises.

    {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": [
        {
          "@type": "Question",
          "name": "Do I need to know how to code?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "No. The workshop is zero code. You describe what you want in plain English and Claude writes it."
          }
        },
        {
          "@type": "Question",
          "name": "What's the refund policy?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "100% refund minus the S$30 Claude Pro gift value if you cancel more than 14 calendar days before the workshop. Within 14 days: no cash refund but one free seat transfer."
          }
        }
      ]
    }
  5. 5.Article (for blog posts)

    Use on every blog post. Pulls double duty for SEO and AEO.

    {
      "@context": "https://schema.org",
      "@type": "Article",
      "headline": "Your post title",
      "description": "Your post description / excerpt.",
      "image": "https://yourdomain.com/blog/post-slug/cover.png",
      "author": {
        "@type": "Person",
        "name": "Your Name",
        "url": "https://yourdomain.com"
      },
      "publisher": {
        "@type": "Organization",
        "name": "Your Business",
        "logo": {
          "@type": "ImageObject",
          "url": "https://yourdomain.com/logo.png"
        }
      },
      "datePublished": "2026-06-06",
      "dateModified": "2026-06-06",
      "mainEntityOfPage": {
        "@type": "WebPage",
        "@id": "https://yourdomain.com/blog/your-post-slug"
      }
    }

    Keep dateModified accurate

    AI engines weight fresher content higher. Update dateModified whenever you meaningfully revise the post — but don't lie. Fake recent dates get caught.

  6. 6.BreadcrumbList (navigational structure)

    Use on any page deeper than the homepage. Helps Google understand site hierarchy.

    {
      "@context": "https://schema.org",
      "@type": "BreadcrumbList",
      "itemListElement": [
        {
          "@type": "ListItem",
          "position": 1,
          "name": "Home",
          "item": "https://yourdomain.com"
        },
        {
          "@type": "ListItem",
          "position": 2,
          "name": "Services",
          "item": "https://yourdomain.com/services"
        },
        {
          "@type": "ListItem",
          "position": 3,
          "name": "Positioning sprint",
          "item": "https://yourdomain.com/services/positioning-sprint"
        }
      ]
    }
  7. 7.ContactPoint (standalone for contact pages)

    Use on /contact pages. Also embed inside Organization schemas.

    {
      "@context": "https://schema.org",
      "@type": "ContactPoint",
      "contactType": "customer support",
      "email": "hello@yourdomain.com",
      "telephone": "+65-1234-5678",
      "areaServed": ["SG", "MY", "ID"],
      "availableLanguage": ["English", "Bahasa", "Mandarin"],
      "hoursAvailable": {
        "@type": "OpeningHoursSpecification",
        "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
        "opens": "09:00",
        "closes": "18:00"
      }
    }
  8. 8.ItemList (for category/listing pages)

    Use on pages that list multiple things — service catalogs, blog indexes, product collections.

    {
      "@context": "https://schema.org",
      "@type": "ItemList",
      "name": "Services",
      "itemListElement": [
        {
          "@type": "ListItem",
          "position": 1,
          "url": "https://yourdomain.com/services/positioning-sprint",
          "name": "Positioning sprint"
        },
        {
          "@type": "ListItem",
          "position": 2,
          "url": "https://yourdomain.com/services/quarterly-pricing-review",
          "name": "Quarterly pricing review"
        }
      ]
    }

Validate before shipping

  • https://search.google.com/test/rich-results — Google's official validator. Paste the URL or the JSON.
  • https://validator.schema.org — Schema.org's reference validator, stricter than Google's.
  • Both should show your schema parsed with zero errors. Warnings are usually safe to ignore.

Troubleshooting

Validator says 'Missing required field' for one of my schemas.

Each schema type has required fields per Schema.org. Person needs at minimum 'name'. Organization needs 'name', 'url'. Article needs 'headline', 'author'. Check the validator's error message and add the missing field.

Google's validator passes but Schema.org's validator fails.

Schema.org is stricter. For real-world SEO/AEO, Google's validation is what matters. If Google passes, you're fine. Schema.org failures are typically about field types being slightly off (e.g. number vs string for prices).

I see 'unrecognized property' warnings.

You used a field name that's not in the schema spec. Often a typo (jobtitle vs jobTitle). Check the schema.org docs page for the type to confirm field names. Some warnings are benign — older or newer fields the validator doesn't know about.

Want to do this with us in the room?

Bring your real project to a full-day workshop and leave with it shipped.

See the workshops