Skip to content

Structured data for article - #6052

Draft
VPS-julia wants to merge 6 commits into
organization-configfrom
structured-data-for-article
Draft

Structured data for article#6052
VPS-julia wants to merge 6 commits into
organization-configfrom
structured-data-for-article

Conversation

@VPS-julia

@VPS-julia VPS-julia commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Adds Article and ItemList structured data (JSON-LD) to the News surfaces in the Demo, so search engines and generative engines (GEO) can identify news articles and news listings. Without it there was no structured data for News. Demo only — the Starter has no News feature.

Builds on the JsonLd component and the site-wide Organization/SiteSettings work.

Surfaces

Surface Schema
News detail page (/news/[slug]) Article
NewsListBlock (curated list on a CMS page) ItemList
News index page (/news) ItemList

Note

The ticket named NewsDetailBlock for the Article, but that block isn't the canonical article page — the /news/[slug] route is. The Article is emitted there so mainEntityOfPage points at the real detail URL.

Details

  • Article: headline, image (absolute DAM URL), datePublished (news.date), dateModified (news.updatedAt), author = publisher = Organization from SiteSettings (falls back to a minimal Organization with the site name when none is configured), mainEntityOfPage. No News entity schema change — there is no per-article author.
  • ItemList: ListItems with position, name and absolute detail URLs, built via createSitePath + siteConfig.url.
  • On /news, only the initially rendered page is encoded; items appended client-side via "Load more" are intentionally not part of the list (documented with a comment).
  • getSiteConfigForDomain/getSiteConfigs were split out of siteConfig.ts into getSiteConfigs.ts so the NewsListBlock loader can build absolute URLs server-side without pulling next/headers into the browser bundle. Existing call sites keep working via a re-export.

Example

The news detail page emits:

{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "est depromo in hic",
  "image": "https://example.com/dam/images/…",
  "datePublished": "2026-07-23T22:49:04.167Z",
  "dateModified": "2026-07-27T09:29:04.191Z",
  "author": { "@type": "Organization", "name": "Comet Site Main" },
  "publisher": { "@type": "Organization", "name": "Comet Site Main" },
  "mainEntityOfPage": "https://example.com/en/news/est-depromo-in-hic"
}

Testing

All three surfaces were validated with validator.schema.org0 errors, 0 warnings for both Article and ItemList (index page and NewsListBlock).

Changeset

None needed — Demo only.

Task: https://vivid-planet.atlassian.net/browse/COM-2968

@VPS-julia VPS-julia self-assigned this Jul 27, 2026
@VPS-julia
VPS-julia changed the base branch from main to organization-config July 27, 2026 10:19
VPS-julia and others added 6 commits July 27, 2026 14:08
`getSiteConfigForDomain`/`getSiteConfigs` are plain env lookups, but they
lived in `siteConfig.ts`, whose top-level `import { headers } from "next/headers"`
taints every importer. That prevents them from being used in code that ends up
in the browser bundle (e.g. block loaders reachable from the block-preview route).

Move the two pure accessors into a dedicated `getSiteConfigs.ts` with no
request-scoped imports, and re-export them from `siteConfig.ts` so existing call
sites keep working unchanged. The header-dependent helpers stay in `siteConfig.ts`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tion

The logic that resolves a DamImageBlock to an absolute image URL was private to
`buildOrganization`. The Article structured data (added next) needs the same
resolution for its `image` field, so extract it into `damImageToAbsoluteUrl`.

Also split `buildOrganization` into `buildOrganizationNode` (the bare
`Organization` object, for nesting as author/publisher) and the context-wrapped
variant used at the page root, and have both take the content scope -- resolving
the site URL from it -- instead of receiving a pre-resolved URL. The layout
passes the scope accordingly. No change to the emitted output.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the builders that produce the JSON-LD for the News surfaces:

- `buildArticle` maps a news item to a schema.org `Article` (headline, image,
  datePublished/dateModified, author/publisher, mainEntityOfPage). Author and
  publisher default to a minimal `Organization` carrying the site name when no
  SiteSettings organization is configured.
- `buildNewsItemList` maps a list of news items to an `ItemList` of `ListItem`s
  with absolute detail URLs.

Both take the content scope and resolve the site config (absolute base URL and
name) from it. Wiring into the surfaces follows in separate commits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Emit schema.org `Article` structured data on the news detail route so search and
generative engines can identify each news article.

Extend the detail fragment with the fields the Article needs (`date`,
`updatedAt`, `slug`) and load the site-wide SiteSettings organization in the page
to fill author/publisher, falling back to the site name when no organization is
configured.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Emit a schema.org `ItemList` for the curated news list rendered by NewsListBlock.

The list is built in the loader rather than the component because the block
renders inside a client component, which has no access to the site config needed
for the absolute item URLs. The loader now returns both the news items and the
prepared structured data.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Emit a schema.org `ItemList` for the `/news` index. Only the initially rendered
page of items is encoded; items appended client-side via "Load more" are
intentionally not part of the list, which is noted with a comment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@VPS-julia
VPS-julia force-pushed the structured-data-for-article branch from ae4b4e5 to 8f428aa Compare July 27, 2026 12:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant