Technical SEO

How We Built Schema Markup
Across 500+ Law Firm Pages

The exact JSON-LD structure, automation approach, and validation process we use to implement schema markup across hundreds of law firm pages without errors.

Reading path

Technical fixes matter most when they support the whole site.

Use technical articles as decision support for crawl cleanup, speed work, schema, and internal linking, then connect them back to the service and audit layer.

14 min read Reading time
2,800 Words
8 FAQs answered
Mar 31, 2026 Last updated

Most law firm websites have no schema markup at all. The ones that do usually have a single Organization block on the homepage, copy-pasted from a WordPress plugin, and nothing else.

That is a missed opportunity. Google uses structured data to understand what a page is about, who wrote it, what services are offered, what questions are answered, and how the business relates to specific locations. When your schema is complete and accurate, you become eligible for the rich results that still apply to your page type, and you make the site much easier for search engines to interpret consistently.

We recently implemented full JSON-LD schema markup across more than 500 law firm pages, including practice area pages, long-form guides, blog articles, tool pages, and location-relevant service pages. Here is exactly how we did it, what mistakes we made along the way, and what the structure looks like.

The problem with doing schema by hand

If you have 5 pages, you can write JSON-LD by hand. Copy the template, change the title, change the URL, change the description. It takes 10 minutes per page.

At 500+ pages, that approach falls apart. You make typos. You forget to update the breadcrumb on one page. You reference an @id that does not exist. You use datePublished: "2026-03-15" instead of the full ISO 8601 format with timezone. Google’s Rich Results Test passes, but the data is inconsistent and some of it is quietly ignored.

The real cost is maintenance. When you change your business name, phone number, or add a new practice area, you have to find and update every single schema block. Miss one and you have conflicting structured data across your site.

How we structured it

Every page on the site gets a JSON-LD @graph array. The reusable page-level entities live as separate siblings linked by @id. Small inline objects are still fine when nothing else needs to reference them, but WebPage, Service, BlogPosting, BreadcrumbList, Organization, and FAQPage should usually stand on their own. This is the part most implementations get wrong.

Here is what a typical practice area page graph looks like:

{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "WebPage",
      "@id": "https://lawfirmseo.pro/industries/seo-for-personal-injury-lawyers/#webpage",
      "name": "SEO for Personal Injury Lawyers",
      "url": "https://lawfirmseo.pro/industries/seo-for-personal-injury-lawyers/",
      "about": { "@id": "https://lawfirmseo.pro/industries/seo-for-personal-injury-lawyers/#service" },
      "breadcrumb": { "@id": "https://lawfirmseo.pro/industries/seo-for-personal-injury-lawyers/#breadcrumb" },
      "isPartOf": { "@id": "https://lawfirmseo.pro/#website" }
    },
    {
      "@type": "Service",
      "@id": "https://lawfirmseo.pro/industries/seo-for-personal-injury-lawyers/#service",
      "name": "SEO for Personal Injury Lawyers",
      "provider": { "@id": "https://lawfirmseo.pro/#organization" }
    },
    {
      "@type": "BreadcrumbList",
      "@id": "https://lawfirmseo.pro/industries/seo-for-personal-injury-lawyers/#breadcrumb",
      "itemListElement": [
        { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://lawfirmseo.pro/" },
        { "@type": "ListItem", "position": 2, "name": "SEO for Personal Injury Lawyers" }
      ]
    },
    {
      "@type": "Organization",
      "@id": "https://lawfirmseo.pro/#organization",
      "name": "LawFirmSEO.pro",
      "url": "https://lawfirmseo.pro/"
    }
  ]
}

Notice what is happening here:

  • WebPage references Service and BreadcrumbList by @id only — they are not embedded inside WebPage
  • Service references Organization by @id only
  • The last breadcrumb item has no item property — this is correct per Google’s spec for the current page
  • Other breadcrumb items use a plain string URL for item, not an object
  • Every entity that gets referenced elsewhere has its own @id

For blog articles, the pattern shifts slightly. BlogPosting replaces Service as the about entity, and we add author, publisher, datePublished, and dateModified:

{
  "@type": "BlogPosting",
  "@id": "https://lawfirmseo.pro/resources/core-web-vitals-law-firm-websites/#article",
  "headline": "Core Web Vitals for Law Firms: What Actually Matters",
  "author": {
    "@type": "Organization",
    "@id": "https://lawfirmseo.pro/#organization",
    "name": "LawFirmSEO.pro",
    "url": "https://lawfirmseo.pro/"
  },
  "publisher": { "@id": "https://lawfirmseo.pro/#organization" },
  "datePublished": "2026-03-15T00:00:00+00:00",
  "dateModified": "2026-03-15T00:00:00+00:00",
  "mainEntityOfPage": { "@id": "https://lawfirmseo.pro/resources/core-web-vitals-law-firm-websites/#webpage" }
}

Dates use full ISO 8601 with timezone offset. Not "2026-03-15". Google accepts the short format but the full format is unambiguous and avoids timezone interpretation issues.

FAQs as separate graph entities

Any page with an FAQ section gets a FAQPage entity as a separate sibling in the @graph array. Not nested inside WebPage. Not nested inside BlogPosting.

{
  "@type": "FAQPage",
  "@id": "https://lawfirmseo.pro/industries/seo-for-personal-injury-lawyers/#faq",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "How long does SEO take for personal injury lawyers?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Most personal injury firms start seeing measurable improvements in organic traffic within 3-6 months..."
      }
    }
  ]
}

As of March 2026, Google limits FAQ rich results to well-known government and health websites. Law firm sites should not expect FAQ dropdowns in the search results. The markup is still worth implementing because it helps Google understand the Q&A content on the page and makes that content explicit in the graph.

The mistakes we made

Mistake 1: Nesting entities inside WebPage. Our first version had the Service entity embedded directly in the WebPage about field as a full object. Google’s testing tool accepted it. But the @id references broke because nested entities do not always resolve correctly when other parts of the graph try to reference them. Moving everything to top-level siblings with @id references fixed it.

Mistake 2: Using bare dates. We started with "datePublished": "2026-03-15" because it is cleaner to read. Google’s Rich Results Test accepted it without warnings. But the structured data documentation specifies ISO 8601 with timezone, and we found inconsistencies in how Google interpreted the dates across different time zones. Switching to "2026-03-15T00:00:00+00:00" eliminated the ambiguity.

Mistake 3: Putting item on the last breadcrumb. The last item in a BreadcrumbList represents the current page. Google’s spec says it should not have an item property — the name alone is sufficient. We had item on every breadcrumb entry, including the last one. The Rich Results Test did not flag it, but Google’s documentation is clear. We removed it.

Mistake 4: Using object format for breadcrumb items. Early versions used "item": { "@type": "WebPage", "@id": "https://..." } for breadcrumb items. The simpler format is a plain string: "item": "https://lawfirmseo.pro/". Both work, but the string format is what Google’s own examples use, and it reduces the overall size of the JSON-LD block.

How we handle it at scale

The site is built on Astro, which means every page is a component that can accept props. We built a schema generation layer that takes page metadata (title, URL, type, dates, FAQs) and outputs the correct @graph array for that page type.

The key insight is that you do not need a different schema template for every page. You need a different template for every page type:

  • Practice area pages get WebPage + Service + BreadcrumbList + Organization + FAQPage
  • Blog articles get WebPage + BlogPosting + BreadcrumbList + Organization + FAQPage
  • Tool pages get WebPage + SoftwareApplication + BreadcrumbList + Organization
  • The homepage gets WebPage + Organization + WebSite

Each template is defined once. Page-specific data comes from frontmatter or props. When we add a new blog article, the schema is generated automatically from the article’s metadata. When we change the organization name, we change it in one place and every page updates.

This is not a plugin. It is about 200 lines of template logic. The output is a <script type="application/ld+json"> block injected into the <head> of every page at build time.

Validating at scale

Google’s Rich Results Test works for one page at a time. That does not scale to 500+ pages.

We built a validation step into our build process. After the site builds, a script extracts every JSON-LD block from every HTML file and checks:

  • Every @id that is referenced in the graph actually exists as a defined entity
  • datePublished and dateModified use the full ISO 8601 format
  • The last breadcrumb item has no item property
  • provider and publisher are @id references, not full objects
  • FAQPage is a top-level graph sibling, not nested
  • No entity is nested inside another entity where it should be a reference

This catches mistakes before they go live. The validation runs in under a second for the entire site.

What we did not do

  • We did not use a WordPress plugin. Those generate generic schema that is usually wrong for legal services pages. The Organization block is fine. Everything else needs to be page-type-specific.
  • We did not use Google Tag Manager to inject schema. Schema should be in the HTML source, not injected by JavaScript. Google says they process JavaScript-rendered structured data, but static HTML is faster and more reliable.
  • We did not add schema types that Google does not use. There are hundreds of schema.org types. Google only supports a few dozen for rich results. We stuck to the types Google understands well in practice and that are useful for search features or entity clarity: WebPage, BlogPosting, Service, FAQPage, BreadcrumbList, Organization, SoftwareApplication.
  • We did not add sameAs links to social profiles we do not actively maintain. Empty or outdated sameAs references are worse than none.

The general pattern

If you are implementing schema across a law firm site with more than a handful of pages:

  1. Define your page types. Most law firm sites have 4-6 types: homepage, practice area, attorney bio, blog article, location page, contact page. Each type gets its own schema template.
  2. Use @graph with separate siblings for reusable entities. Keep the major page-level entities separate and connect them with @id references.
  3. Automate from metadata. Page title, URL, dates, and FAQs should drive schema generation. Do not maintain schema blocks by hand.
  4. Validate after every build. A simple script that checks @id references and date formats will catch 90% of schema errors.
  5. Test with Google’s tools, but do not trust them completely. The Rich Results Test accepts a lot of technically-valid-but-suboptimal markup. Your own validation should enforce stricter rules.

Schema markup is not glamorous work. But for a law firm competing in local search, it is the difference between Google understanding your pages and Google guessing about them. At 500+ pages, the compounding effect is significant.

Need a clearer next move?

Get a Free Schema Markup Audit

We'll check every page on your law firm's site for structured data errors, missing entities, and optimization opportunities. Full report with corrected JSON-LD included.

Next steps

Use this topic inside the right part of your growth system.

The strongest next move is usually a technical service review, a deeper implementation guide, or a tool that helps you validate the basics.

Related reads

Other articles firms usually read next.

These are the closest matches by topic, so the next click keeps building useful context instead of sending you sideways.

Frequently asked questions

Technical SEO FAQ

Quick answers to the most common questions about this topic.

01

Why does schema markup matter for law firm websites?

Google uses structured data to understand what a page is about, who wrote it, what services are offered, what questions are answered, and how the business relates to specific locations. When your schema is complete and accurate, you become eligible for the rich results that still apply to your page type, and you make the site much easier for search engines to interpret consistently. For law firms competing in local search, it is the difference between Google understanding your pages and Google guessing about them.

02

What is the correct JSON-LD structure for law firm pages?

Every page should use a JSON-LD @graph array with reusable page-level entities as separate siblings linked by @id. A typical practice area page includes WebPage, Service, BreadcrumbList, Organization, and FAQPage as separate @graph siblings. Small inline objects are still fine when nothing else needs to reference them, but the major page-level entities should stand on their own. This structure makes relationships explicit and easier to validate at scale.

03

Should I nest schema entities inside WebPage?

Not for the major reusable entities. WebPage, Service, BlogPosting, BreadcrumbList, Organization, and FAQPage should usually be top-level siblings in the @graph array, connected by @id references. Small inline objects can still be fine, but embedding full Service or BlogPosting entities inside WebPage makes large implementations harder to validate and maintain.

04

What date format should I use in schema markup?

Use full ISO 8601 with timezone offset: 2026-03-15T00:00:00+00:00. Never use the bare date format like 2026-03-15. Google accepts the short format, but the full format is unambiguous and avoids timezone interpretation issues across different regions.

05

How should breadcrumb schema be structured for law firm pages?

The last item in a BreadcrumbList represents the current page and should not have an item property. Only the name is needed. All other breadcrumb items should use a plain string URL for the item field, not an object. For example: position 1 has name Home with item as the homepage URL string, and the final position has only a name with no item property.

06

Should I use a WordPress plugin for law firm schema markup?

WordPress schema plugins generate generic markup that is usually wrong for legal services pages. The Organization block is typically fine, but everything else needs to be page-type-specific. Practice area pages need Service entities, blog articles need BlogPosting entities, and location pages need LocalBusiness entities. A plugin that outputs the same generic schema on every page misses the value of structured data entirely.

07

How do I validate schema markup across many pages?

Google's Rich Results Test works for one page at a time and does not scale. Build a validation step into your build process that extracts every JSON-LD block from every HTML file and checks that every referenced @id exists as a defined entity, dates use full ISO 8601 format, the last breadcrumb has no item property, provider and publisher are @id references, and FAQPage is a top-level graph sibling. This catches mistakes before they go live.

08

Should I inject schema markup through Google Tag Manager?

No. Schema should be in the HTML source, not injected by JavaScript. Google says they process JavaScript-rendered structured data, but static HTML is faster and more reliable. Injecting schema through GTM adds a dependency on JavaScript execution and introduces a delay before Google can read the structured data. Put your JSON-LD directly in the page source.

Next step

Get Your Schema Markup Done Right

Book a free strategy session. We'll audit your current structured data, identify what's missing, and show you how proper schema implementation can improve your search visibility.

Book my strategy call Free Schema Generator
No obligation 100% confidential Custom roadmap included