JSON-LD Schema Markup: Complete SEO Implementation Guide

Last Update 3/28/2026
Structured data Rich results

JSON-LD structured data tells search engines exactly what your content is about. The right schema markup unlocks rich results in Google Search — FAQ dropdowns, article carousels, breadcrumb trails, and more — directly from the search results page.

This guide covers every major schema type with real C# implementation code and the exact JSON output it produces. Every example is taken directly from a production Blazor codebase.

Illustration showing JSON-LD structured data feeding rich results in Google Search including FAQ dropdowns, article carousels and breadcrumb navigation
JSON-LD structured data is the most maintainable way to add schema markup — it lives in a script tag, completely separate from your HTML.

What is JSON-LD and why it matters for SEO

JSON-LD (JavaScript Object Notation for Linked Data) is a method of encoding structured data using the JSON format. Search engines such as Google read this data to understand the meaning of your content, not just its keywords.

When Google can parse your structured data it becomes eligible for rich results — visually enhanced search listings that display ratings, FAQ dropdowns, article images, breadcrumb trails, and other features directly in the search results page. Rich results consistently achieve higher click-through rates than plain blue links.

Google officially recommends JSON-LD over Microdata and RDFa because it can be placed anywhere in the page — including the head — and does not require modifying existing HTML elements. The structured data is completely decoupled from your markup.

Rich result eligibility

Valid schema markup makes your pages eligible for FAQ dropdowns, article carousels, breadcrumb trails, and other rich features in Google Search.

More clicks from the same ranking

Semantic clarity

Structured data removes ambiguity. A page about a question-and-answer topic is explicitly declared as a FAQPage, so Google knows to render it as a FAQ rich result.

Tell search engines exactly what you have

Zero HTML changes

JSON-LD lives in a script tag in the head. It never touches your visual HTML, so adding or updating schema never risks breaking layouts or accessibility.

Clean separation of concerns

JSON-LD vs. Microdata vs. RDFa

Three formats exist for embedding schema.org structured data in web pages. All three are understood by Google, but they differ significantly in how they are applied and maintained.

Format Syntax location Maintenance SEO impact Google recommendation
JSON-LD Separate script tag Easy — no HTML changes Full rich result support Recommended
Microdata Inline HTML attributes High — tightly coupled to markup Full rich result support Supported
RDFa Inline HTML attributes High — tightly coupled to markup Full rich result support Supported

Why JSON-LD wins on maintainability

With Microdata and RDFa, schema attributes are scattered throughout your HTML. A design change that restructures a template can silently break structured data. JSON-LD is a self-contained block — update the script once and no HTML is affected.

Web Development learning pick

Master Modern Web Development with SEO Best Practices

Tip from me I found this course valuable for understanding how technical SEO, structured data, and modern web standards work together in real-world projects.

  • Learn HTML meta tags, Open Graph, and JSON-LD structured data implementation.
  • Practical SEO strategies for better search engine visibility and ranking.
  • Hands-on projects covering responsive design and performance optimization.
Explore the Web Development course

Core schema types and when to use them

Schema.org defines hundreds of types but a handful cover the majority of web content. These are the types that unlock rich results in Google Search.

Schema type Use case Rich result type
Article Blog posts, guides, news articles Article carousel, Top stories
FAQPage Pages with question/answer pairs FAQ dropdown in search results
BreadcrumbList Any page with a navigation hierarchy Breadcrumb trail below the URL
WebSite / WebPage Homepage, hub pages, landing pages Sitelinks search box, entity recognition
SoftwareApplication Apps, tools, software products App rich result with rating and price

Schema.org vocabulary

All schema types are defined at schema.org. The @context property in every JSON-LD block tells parsers to interpret property names against the schema.org vocabulary. You can combine multiple schema types on a single page — Google processes each script block independently.

Article schema implementation

Article schema is the most important type for content-driven sites. It tells Google the headline, description, publication date, author, and publisher of a piece of content — the minimum set of properties required for rich result eligibility.

The C# class below is the serialization model. Each property maps directly to the corresponding JSON-LD field using JsonPropertyName attributes.

C#ArticleSchema C# class (JsonLdService.cs)
private class ArticleSchema
{
    [JsonPropertyName("@context")]
    public string Context { get; } = "https://schema.org";

    [JsonPropertyName("@type")]
    public string Type { get; } = "Article";

    [JsonPropertyName("headline")]
    public string? Headline { get; set; }

    [JsonPropertyName("description")]
    public string? Description { get; set; }

    [JsonPropertyName("image")]
    public string? Image { get; set; }

    [JsonPropertyName("url")]
    public string? Url { get; set; }

    [JsonPropertyName("author")]
    public Organization? Author { get; set; }

    [JsonPropertyName("publisher")]
    public ArticlePublisher? Publisher { get; set; }

    [JsonPropertyName("datePublished")]
    public string? DatePublished { get; set; }

    [JsonPropertyName("dateModified")]
    public string? DateModified { get; set; }

    [JsonPropertyName("articleSection")]
    public List<string>? ArticleSection { get; set; }
}

The required properties are: headline, image, datePublished, dateModified, author, and publisher. The articleSection array is optional but recommended — it signals the article's topical scope to Google.

HTMLArticle schema JSON-LD output
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "JSON-LD Schema Markup: Complete SEO Implementation Guide",
  "description": "Learn how to implement JSON-LD structured data for Article, FAQPage, BreadcrumbList, WebSite, and WebPage schema types.",
  "image": "https://ghostlyinc.com/images/web/jsonldguide/opengraph/hero-1200.png",
  "url": "https://ghostlyinc.com/en/json-ld-schema-markup-seo-guide/",
  "author": {
    "@type": "Organization",
    "name": "GhostlyInc",
    "url": "https://ghostlyinc.com"
  },
  "publisher": {
    "@type": "Organization",
    "name": "GhostlyInc",
    "logo": {
      "@type": "ImageObject",
      "url": "https://ghostlyinc.com/images/logo.png"
    }
  },
  "datePublished": "2026-03-28T10:00:00Z",
  "dateModified": "2026-03-28T10:00:00Z",
  "articleSection": [
    "What is JSON-LD and why it matters for SEO",
    "JSON-LD vs. Microdata vs. RDFa",
    "Core schema types and when to use them",
    "Article schema implementation"
  ]
}
</script>

The author and publisher are both typed as Organization. For personal blogs, the author can instead be a Person with a name and url.

FAQ schema for rich snippets

FAQPage schema creates FAQ rich results — expandable question/answer pairs displayed directly in Google Search results below your page listing. This can significantly increase the vertical space your result occupies, improving visibility and click-through rates.

The FaqPage class holds a list of question entities. Each entity must have a name (the question) and an acceptedAnswer with a text property (the answer).

C#FaqPage C# class (JsonLdService.cs)
private class FaqPage
{
    [JsonPropertyName("@context")]
    public string Context { get; } = "https://schema.org";

    [JsonPropertyName("@type")]
    public string Type { get; } = "FAQPage";

    [JsonPropertyName("@id")]
    public string? Id { get; set; }

    [JsonPropertyName("name")]
    public string? Name { get; set; }

    [JsonPropertyName("mainEntity")]
    public List<FaqEntity>? MainEntity { get; set; }
}

private class FaqEntity
{
    [JsonPropertyName("@type")]
    public string Type { get; } = "Question";

    [JsonPropertyName("name")]
    public string? Name { get; set; }

    [JsonPropertyName("acceptedAnswer")]
    public FaqAnswer? AcceptedAnswer { get; set; }
}

private class FaqAnswer
{
    [JsonPropertyName("@type")]
    public string Type { get; } = "Answer";

    [JsonPropertyName("text")]
    public string? Text { get; set; }
}

The output is a single FAQPage block with a mainEntity array. Each item is a Question type with an acceptedAnswer. Google supports up to ten questions per page for rich results.

HTMLFAQ schema JSON-LD output
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "@id": "https://ghostlyinc.com/en/json-ld-schema-markup-seo-guide/#faq",
  "name": "Frequently asked questions",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What is the difference between JSON-LD and Microdata?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "JSON-LD is a separate script block that lives in the head and does not touch HTML. Microdata adds attributes directly to HTML elements. Google recommends JSON-LD for its maintainability."
      }
    },
    {
      "@type": "Question",
      "name": "How many FAQ questions can I mark up?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Google supports up to ten questions per page for the FAQ rich result. Additional questions are still valid schema but will not appear in the search result enhancement."
      }
    }
  ]
}
</script>

The @id on the FAQPage block anchors the schema to a specific URL fragment. This is best practice for pages that contain multiple schema blocks, as it allows parsers to unambiguously identify each entity.

WebSite and WebPage schema

WebSite and WebPage schema establish the foundational entity graph for a site. WebSite identifies the site itself — its name, URL, language, and publisher. WebPage describes the individual page and links it to the parent WebSite using the isPartOf relationship.

Both classes follow the same JSON-LD pattern. The @id on WebSite is the domain root; the @id on WebPage is the full page URL.

C#WebSiteSchema and WebPageSchema C# classes (JsonLdService.cs)
private class WebSiteSchema
{
    [JsonPropertyName("@context")]
    public string Context { get; } = "https://schema.org";

    [JsonPropertyName("@type")]
    public string Type { get; } = "WebSite";

    [JsonPropertyName("name")]
    public string? Name { get; set; }

    [JsonPropertyName("url")]
    public string? Url { get; set; }

    [JsonPropertyName("inLanguage")]
    public string? InLanguage { get; set; }

    [JsonPropertyName("publisher")]
    public Organization? Publisher { get; set; }
}

private class WebPageSchema
{
    [JsonPropertyName("@context")]
    public string Context { get; } = "https://schema.org";

    [JsonPropertyName("@type")]
    public string Type { get; } = "WebPage";

    [JsonPropertyName("@id")]
    public string? Id { get; set; }

    [JsonPropertyName("name")]
    public string? Name { get; set; }

    [JsonPropertyName("description")]
    public string? Description { get; set; }

    [JsonPropertyName("url")]
    public string? Url { get; set; }

    [JsonPropertyName("inLanguage")]
    public string? InLanguage { get; set; }

    [JsonPropertyName("isPartOf")]
    public WebSiteReference? IsPartOf { get; set; }
}

The inLanguage property uses BCP 47 language codes (e.g. en, de, fr). The isPartOf link ties the WebPage to the WebSite entity by referencing the domain's @id. This helps Google build a complete entity model of your site.

HTMLWebSite and WebPage schema JSON-LD output
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "WebSite",
  "name": "GhostlyInc",
  "url": "https://ghostlyinc.com",
  "inLanguage": "en",
  "publisher": {
    "@type": "Organization",
    "name": "GhostlyInc",
    "url": "https://ghostlyinc.com"
  }
}
</script>

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "WebPage",
  "@id": "https://ghostlyinc.com/en/json-ld-schema-markup-seo-guide/",
  "name": "JSON-LD Schema Markup: Complete SEO Implementation Guide",
  "description": "Learn how to implement JSON-LD structured data for Article, FAQPage, BreadcrumbList, WebSite, and WebPage schema types.",
  "url": "https://ghostlyinc.com/en/json-ld-schema-markup-seo-guide/",
  "inLanguage": "en",
  "isPartOf": {
    "@type": "WebSite",
    "@id": "https://ghostlyinc.com"
  }
}
</script>

Implementation best practices

The WrapInScriptTag helper method is the single point where JSON strings become valid JSON-LD blocks. It wraps any serialized schema payload in the required script tag with the application/ld+json MIME type.

This is the method that all schema builders in the service call — Article, FAQPage, BreadcrumbList, WebSite, and WebPage all pass their serialized JSON to this same utility.

C#WrapInScriptTag method (JsonLdService.cs)
private static MarkupString WrapInScriptTag(string json)
{
    var builder = new StringBuilder();
    builder.AppendLine("<script type=\"application/ld+json\">");
    builder.AppendLine(json);
    builder.AppendLine("</script>");

    return new MarkupString(builder.ToString());
}

The SchemaContext object is the entry point for the BuildSchema method. It carries all the data needed to build the full set of schema blocks for a page — type, metadata, FAQ list, breadcrumb hierarchy, and article data.

C#BuildSchema dispatch logic (JsonLdService.cs)
public SchemaBuildResult BuildSchema(SchemaContext context)
{
    if (context == null) throw new ArgumentNullException(nameof(context));

    var jsonLd = new List<MarkupString>();

    switch (context.PageType)
    {
        case PageSchemaType.Home:
            jsonLd.Add(WebSite(context));
            jsonLd.Add(WebPage(context));
            break;
        case PageSchemaType.Article:
            jsonLd.Add(Article(context.Article!));
            break;
        case PageSchemaType.Tool:
            jsonLd.Add(WebPage(context));
            jsonLd.Add(SoftwareApplication(context.SoftwareApplication!));
            break;
        case PageSchemaType.Hub:
            jsonLd.Add(WebPage(context));
            break;
    }

    if (context.Faqs != null && context.Faqs.Count > 0)
    {
        jsonLd.Add(FAQ(context.Faqs.ToList(), context.FaqTitle, context.Url));
    }

    if (context.ParentHierarchy.Count > 0)
    {
        jsonLd.Add(BreadcrumbList(BuildBreadcrumbItems(context)));
    }

    return new SchemaBuildResult(jsonLd, context.PageType == PageSchemaType.Article);
}

The dispatch switch makes the schema builder composable. FAQs and breadcrumbs are always appended if present, regardless of page type. This means any page type can have a FAQ block or breadcrumb trail added without changing the core dispatch logic.

Testing and validating your schema

Valid JSON-LD syntax does not guarantee rich result eligibility. Google's tools check both syntax validity and whether your content meets the policy requirements for each rich result type.

Rich Results Test

Use search.google.com/test/rich-results to test any URL or paste raw HTML. Google confirms which rich result types were detected and highlights any missing required properties.

Google's official eligibility checker

Schema Markup Validator

validator.schema.org checks your markup against the schema.org specification independently of Google's rich result policies. Useful for catching property name typos and type mismatches.

Schema.org syntax validation

Google Search Console

The Enhancements section in Search Console reports schema errors and warnings at scale across your indexed pages. Use it for ongoing monitoring after initial validation.

Monitor at scale post-launch

Frequently asked questions

Answers to common JSON-LD schema markup questions