[ ENGINEERING_GUIDE ][ JSON-LD ][ SCHEMA.ORG ][ SEO ][ NEXTJS ]

JSON-LD and Schema.org — business website SEO in Next.js (2026 guide)

June 10, 20269 min read
Author: DevStudio.itWeb & AI Studio

Organization, WebSite, BreadcrumbList, BlogPosting and FAQPage — App Router implementation, getStructuredData, hreflang and Rich Results testing for multilingual service sites.

READ_TIME: 9 MIN_COMPLEXITY: MED_
STAMP: VERIFIED_BY_DS_

TL;DR

JSON-LD is the simplest way to tell Google your site is a service business with a blog—not a random pile of divs. On production Next.js 15 (App Router), five types cover most cases: Organization and WebSite in the locale layout (getStructuredData in src/lib/structured-data.ts), BreadcrumbList + BlogPosting on blog articles, and FAQPage when the markdown includes a FAQ section. Each language version (/pl, /en, /de) needs its own JSON-LD with correct inLanguage and hreflang in metadata—not one global English block. Test with Rich Results Test and Search Console, not only “is the script in the DOM?”.

Who this is for

  • Service companies with a multilingual site on Next.js App Router
  • Developers implementing technical SEO without WordPress plugins
  • Teams that already have meta title/description but lack rich snippets and a consistent organization entity
  • Corporate blog authors who want proper BlogPosting, not just og:article

Keywords (SEO)

json-ld schema.org business website, structured data nextjs 2026, organization website breadcrumblist blogposting, faqpage seo, google rich results test, hreflang json-ld

Why JSON-LD, not microdata in HTML

Google officially supports three formats: JSON-LD, Microdata, and RDFa. In React/Next.js, JSON-LD in <script type="application/ld+json"> almost always wins:

Format Pros Cons in Next.js
JSON-LD Separate from JSX, easy to generate from data One JSON error = entire block ignored
Microdata “Visible” in HTML Mixing attributes into components, painful refactors
RDFa Rarely used Low adoption in the React ecosystem

JSON-LD does not replace good content—but without it, Google often cannot tell that DevStudio.it is the same company as the logo in the footer, or that /en/blog/... is a blog post, not a generic page.

Five types that actually help a business site

1. Organization — the company entity

Organization describes the legal/marketing entity: name, logo, contact, services, offers. In the DevStudio.it project, getStructuredData(locale) in src/lib/structured-data.ts returns an object with @type: Organization, @id (https://devstudioit.com/#organization), contactPoint, sameAs, a service list, and offers.

Key fields for a service business:

  • @id — stable identifier; other types reference it (publisher, provider)
  • logo as ImageObject with width/height — Google requires a clear logo for some rich results
  • contactPoint — phone, email, areaServed, availableLanguage
  • sameAs — LinkedIn, GitHub, Twitter — reinforces entity identity

Note: aggregateRating on Organization is often audited—if you do not have real reviews aligned with Google policy, do not add hard-coded stars. In this repo, reviews come from getReviewStructuredData built from on-page testimonials.

2. WebSite — site entity and SearchAction

WebSite ties the domain to the organization: name, url, inLanguage, publisher. Optionally SearchAction via potentialAction if you have search (/search?q=).

In getStructuredData, inLanguage includes ['pl', 'en', 'de']—a signal the site is multilingual. The WebSite url points to the locale root (https://devstudioit.com/en, etc.), not only the bare domain.

3. BreadcrumbList — path in search results

BreadcrumbList is a list of itemListElement entries 1…n. On the blog, src/app/[locale]/blog/[slug]/page.tsx builds:

  1. DevStudio.it → https://devstudioit.com/{locale}
  2. Blog → https://devstudioit.com/{locale}/blog
  3. Article title → post canonical URL

Each item has @type: ListItem, position, name, and item (full URL). UI breadcrumbs and JSON-LD should match the same hierarchy—mismatches do not always penalize, but they complicate Search Console debugging.

4. BlogPosting — an article, not a generic WebPage

For blog posts use BlogPosting (or Article), not a generic WebPage as the main entity. The articleJsonLd object includes:

  • headline, description, datePublished, dateModified
  • author (Person or Organization with author URL)
  • publisher with Organization logo
  • mainEntityOfPage pointing to canonical
  • image — absolute OG URL (https://devstudioit.com/og-image-large.png or per-post ogImage)
  • inLanguagepl-PL, en-US, de-DE
  • wordCount — computed from markdown body
  • speakable — optional cssSelector for voice assistants

Canonical must be identical to alternates.canonical in generateMetadata—the same string in JSON-LD and <link rel="canonical">.

5. FAQPage — only when FAQ is in the content

Google’s FAQPage requires real questions and answers visible to users. In this project, getBlogFaq(post.content) parses the ## FAQ section in markdown—each ### Question plus answer paragraph.

FAQ only in JSON-LD with nothing on the page risks a manual action for structured data spam. Always: content first, JSON-LD second.

Implementation in Next.js App Router

Locale layout — Organization + WebSite

In src/app/[locale]/layout.tsx, after getStructuredData(locale), inject JSON-LD scripts:

<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{
    __html: JSON.stringify(structuredData.organization),
  }}
/>
<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{
    __html: JSON.stringify(structuredData.website),
  }}
/>

Additionally hostingService as Service and getReviewStructuredData from testimonials extend the company entity with a concrete hosting offer.

Server Components — generate JSON-LD on the server; no useEffect. Important for crawlers and first HTML from the edge.

Blog post page — multiple JSON-LD blocks

On [slug]/page.tsx, a typical set is four scripts:

  1. BreadcrumbList
  2. BlogPosting
  3. FAQPage (conditional when faqItems.length > 0)
  4. WebPage with mainEntity pointing to #article

Multiple blocks are valid—Google connects entities via @id and URL. Alternative: one @graph with all types; in Next.js separate <script> tags are often clearer.

getStructuredData — one file, three locales

src/lib/structured-data.ts holds organization translations (services, offers, description) and builds full schema.org objects. Pattern:

  • translations keyed by pl | en | de
  • baseUrl = 'https://devstudioit.com'
  • locale URL: pl/pl, others → /{locale}

When you add a new service on the marketing site, update structured data too so Google sees a catalog consistent with landing pages.

hreflang and JSON-LD — parallel signals

hreflang lives in HTML metadata (alternates.languages in Next.js), not inside JSON-LD. For the blog, getBlogPostHreflangLanguages links posts via translationId in frontmatter—the same translationId across all three markdown files.

Signal Where What it tells Google
hreflang <link rel="alternate" hreflang="..."> “This content has PL/EN/DE equivalents”
inLanguage in BlogPosting JSON-LD “This article is in English”
Canonical meta + JSON-LD mainEntityOfPage “Official URL for this version”

Failure mode: hreflang points to /en/blog/wrong-slug while canonical and JSON-LD use another slug—split signals, often no rich result despite valid JSON.

For the business homepage, ensure each locale has Organization with url https://devstudioit.com/{locale}, not only an English description in JSON-LD on /pl.

Testing — Rich Results and beyond

Google Rich Results Test

  1. Open Rich Results Test.
  2. Enter a production URL (e.g. https://devstudioit.com/en/blog/json-ld-schema-org-seo-strona-firmowa-2026).
  3. Check detected types: Article/BlogPosting, FAQ (if present), Breadcrumb.
  4. Fix critical errors (missing image, bad date format).

The test fetches rendered HTML—works on Vercel production. Preview URLs often differ in env and robots—test structured data on devstudioit.com.

Schema Markup Validator (schema.org)

The schema.org validator checks spec compliance, not only Google rich results. Useful for complex Organization with offers and shippingDetails.

Search Console

After deploy: Enhancements → FAQ, Articles (if eligible). “Valid” does not guarantee a rich snippet UI—Google decides per query—but “Error” always needs a fix.

DevTools — practical checklist

  • View Source (not only Elements)—JSON-LD must be in first HTML
  • JSON.parse script content—one trailing comma breaks the block
  • Compare FAQ count in DOM with FAQPage mainEntity

Common mistakes on business sites

  1. One Organization for the whole domain without locale—weaker signal for DE/EN.
  2. Duplicate BlogPosting—plugin + manual script = two @type: BlogPosting with different dates.
  3. FAQPage without visible FAQ—content in display:none is also problematic.
  4. Relative URLs in image and item—schema requires absolute https://.
  5. author without URL—Person with url to the author page strengthens E-E-A-T.
  6. dateModified always equals datePublished—content updates without changing updated in frontmatter look stale.

Extensions — when to add more types

Type When Note
LocalBusiness Physical location + map, NAP Needs address and hours; do not confuse with Organization
Service Service landing (websites) Per service with provider → Organization @id
HowTo Step-by-step articles In repo: getHowToStructuredData on homepage
SoftwareApplication SaaS in portfolio Separate function in structured-data.ts

Do not add everything on every page—relevancy matters. Contact page ≠ Product unless you sell a product there.

Performance and security

JSON-LD is lightweight (a few to tens of KB). Do not load it from an external API at runtime—generate statically with SSG/ISR. dangerouslySetInnerHTML is intentional here; data comes from your code, not user input on the blog (frontmatter lives in the repo).

For very large Organization objects with dozens of offers, consider trimming to top offers—smaller HTML, same business signal.

JSON-LD on service landing pages

Beyond the blog, emit structured data on landings such as /en/strony-www. Minimum: WebPage with about pointing to Service or Organization @id. If the landing describes a service with a “from $X” price, an Offer with priceCurrency and provider strengthens service SEO—without fake LocalBusiness when the company is remote-first.

In getStructuredData, the service list in Organization should match navigation menu services. Mismatch—“chatbots in menu, only websites in JSON-LD”—weakens entity consistency. When shipping a new landing: (1) content + meta, (2) hreflang if multilingual, (3) Service JSON-LD or WebPage linked to global Organization.

Structured data audit — 15 minutes before launch

  1. Homepage per locale—Rich Results Test: Organization + WebSite.
  2. One blog article—BlogPosting + Breadcrumb + FAQ (if present).
  3. One service landing—WebPage/Service.
  4. Search Console → URL Inspection → detected structured data.
  5. Compare JSON-LD dateModified with frontmatter updated after content edits.

Save results in the deploy ticket—makes it easier to decide whether a PR broke SEO.

FAQ

Will Google always show stars and FAQ in results?

No. Structured data is eligibility, not a UI guarantee. Google has limited FAQ rich results in recent years—still worth correct FAQPage for AEO and alternate formats, but do not expect a magical CTR jump.

One JSON-LD script or many?

Both OK. Google merges entities. In Next.js multiple <script type="application/ld+json"> tags are clearer with conditional FAQPage.

Must JSON-LD be in <head>?

Google reads JSON-LD in head and body. App Router often renders scripts in page components—it works if they appear in SSR HTML.

Use publisher: { '@type': 'Organization', name: '...', logo: ... } and the global Organization @id. Optionally isPartOf: { '@type': 'WebSite', '@id': '...' }.

Does hreflang replace translating JSON-LD?

No. hreflang links URLs across languages; JSON-LD describes content under one URL. You need both.

Summary

Business site SEO in 2026 adds an entity layer (Organization, WebSite) plus per-page types (BreadcrumbList, BlogPosting, FAQPage). In Next.js App Router, keep generators in src/lib/structured-data.ts and blog page.tsx, sync canonical, hreflang, and inLanguage, and test on production with Rich Results Test. Without visible FAQ content, do not emit FAQPage—structured data supports marketing; it does not replace honest content.

Want structured data implemented for your site?

  • Contact us — JSON-LD and hreflang audit for your domain
  • Websites — Next.js, technical SEO and rich results from day one
  • Blog — more guides on performance and conversion

About the author

We build fast websites, web/mobile apps, AI chatbots and hosting setups — with a focus on SEO and conversion.

Recommended links

From theory to production — Branchly, our hosting stack, care plans and shipped work.

LIKE HOW WE THINK? LET'S BUILD SOMETHING TOGETHER.

[ START_PROJECT_CONFIGURATION ]