Skip to content

Structured data for article - #6052

Open
VPS-julia wants to merge 6 commits into
mainfrom
structured-data-for-article
Open

Structured data for article#6052
VPS-julia wants to merge 6 commits into
mainfrom
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 and generative engines can identify news articles and listings. There was no structured data for News before. Demo only — the Starter has no News feature.

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

author and publisher reuse the site-wide Organization from the site config (#6038). To nest it, that mapping moved out of OrganizationJsonLd into buildOrganizationNode; the component now renders the same node wrapped in @context, so its output is unchanged.

Two smaller notes:

  • On /news, only the initially rendered page is encoded — items appended via "Load more" are intentionally left out.
  • getSiteConfigForDomain/getSiteConfigs moved into getSiteConfigs.ts so the NewsListBlock loader can build absolute URLs without pulling next/headers into the browser bundle. Existing call sites keep working via a re-export.

Example — actual output from /en/news/tego-coepi-valde-cattus:

{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "tego coepi valde cattus",
  "image": "http://localhost:3000/dam/images/b2bdb7ce.../resize:1024:1024/comet",
  "datePublished": "2026-09-07T22:40:36.720Z",
  "dateModified": "2026-09-09T11:43:11.444Z",
  "author": { "@type": "Organization", "name": "Vivid Planet Software GmbH", "url": "https://www.vivid-planet.com", "…": "" },
  "publisher": { "…": "same Organization node" },
  "mainEntityOfPage": "http://localhost:3000/en/news/tego-coepi-valde-cattus"
}

Testing

Tested locally by checking structured data in the dom:
Screenshot 2026-09-09 at 15 29 11

Task: https://vivid-planet.atlassian.net/browse/DEX-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
VPS-julia force-pushed the structured-data-for-article branch from ae4b4e5 to 8f428aa Compare July 27, 2026 12:10
Base automatically changed from organization-config to main September 8, 2026 07:37
`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 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJR1y9TUydWJ4ddW8cjG3q
The Article structured data (added next) needs the site's `Organization` as a
nested `author`/`publisher` node, i.e. the same mapping from the site config that
`OrganizationJsonLd` already does, but without the `@context` wrapper.

Move the mapping into `buildOrganizationNode`, add a context-wrapped
`buildOrganization` for use at the page root, and have `OrganizationJsonLd`
render that. No change to the emitted output.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJR1y9TUydWJ4ddW8cjG3q
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 reuse the site-wide `Organization` node from the site config.
- `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
organization) from it. `damImageToAbsoluteUrl` resolves a `DamImageBlock` to the
absolute image URL the `Article` needs. Wiring into the surfaces follows in
separate commits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJR1y9TUydWJ4ddW8cjG3q
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`). Author and publisher come from the site config's
organization, so no additional query is needed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJR1y9TUydWJ4ddW8cjG3q
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 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJR1y9TUydWJ4ddW8cjG3q
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 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJR1y9TUydWJ4ddW8cjG3q
@VPS-julia
VPS-julia force-pushed the structured-data-for-article branch from 8f428aa to 345a882 Compare September 9, 2026 11:03
@vivid-planet vivid-planet deleted a comment from coderabbitai Bot Sep 9, 2026
@VPS-julia
VPS-julia marked this pull request as ready for review September 9, 2026 13:32
@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Advanced

Run ID: 29dc637d-95e2-4826-903e-49d39bcab46d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions
github-actions Bot requested a review from VPS-Obi September 9, 2026 13:32
@greptile-apps

greptile-apps Bot commented Sep 9, 2026

Copy link
Copy Markdown

Greptile Summary

Adds JSON-LD structured data for Demo news detail pages, news index pages, and curated news-list blocks.

  • Builds Article metadata with article dates, image, canonical page URL, author, and publisher.
  • Builds ItemList metadata for initially rendered news listings.
  • Extracts reusable organization and site-configuration builders without changing the existing organization output.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
demo/site/src/util/structuredData/buildArticle.ts Builds contextual Article JSON-LD from news content, site configuration, and the shared organization node.
demo/site/src/util/structuredData/buildNewsItemList.ts Builds absolute ItemList entries while preserving the accepted single-scope behavior of the news surfaces.
demo/site/src/news/blocks/NewsListBlock.loader.ts Returns news and server-built structured data for curated news-list blocks.
demo/site/src/organization/OrganizationJsonLd.tsx Delegates the existing Organization JSON-LD mapping to the reusable organization builder.
demo/site/src/util/getSiteConfigs.ts Extracts environment-backed site-configuration access so structured-data builders avoid server header dependencies.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[News detail page] --> B[buildArticle]
    B --> C[Article JSON-LD]
    D[News index page] --> E[buildNewsItemList]
    F[NewsListBlock loader] --> E
    E --> G[ItemList JSON-LD]
    H[Site configuration] --> B
    H --> E
    H --> I[buildOrganizationNode]
    I --> B
Loading

Reviews (3): Last reviewed commit: "demo: Add ItemList JSON-LD to the news i..." | Re-trigger Greptile

Comment thread demo/site/src/util/structuredData/buildNewsItemList.ts
@nsams

nsams commented Sep 9, 2026

Copy link
Copy Markdown
Member

@greptile review

Comment on lines +34 to +35
author: organization,
publisher: organization,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imho we should not include the same organisation object, that we already include on every page, here as author and publisher. I'd use auther/publisher ONLY if we would have real values (and we would store the author per news - and display them in the site)


let siteConfigs: PublicSiteConfig[];

export function getSiteConfigs() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please keep changes in a PR to a minimum and move this refactor (move of getSiteConfigs/getSiteConfigForDomain to it's own file) to an individual PR

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this refactor makes sense. Here's the rationale from the PR description:

getSiteConfigForDomain/getSiteConfigs moved into getSiteConfigs.ts so the NewsListBlock loader can build absolute URLs without pulling next/headers into the browser bundle. Existing call sites keep working via a re-export.

But process.env.PUBLIC_SITE_CONFIGS isn't available in the browser, why do we need to move it then?

))}
</ol>
<>
{structuredData && <JsonLd<ItemList> data={structuredData} />}

@nsams nsams Sep 9, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it was required in the ticket, but I'm questioning this.
I don't think we should use an ItemList JsonLd here when we have a detail page that has a full Article JsonLd

Comment on lines +11 to +14
if ("urlTemplate" in props && props.damFile?.image) {
const { width, height } = props.damFile.image;
return generateImageUrl({ src: props.urlTemplate, width }, width / height);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This generates an image in its original size and aspect ratio. Google recommends to generate multiple images:

For best results, we recommend providing multiple high-resolution images (minimum of 50K pixels when multiplying width and height) with the following aspect ratios: 16x9, 4x3, and 1x1.
https://developers.google.com/search/docs/appearance/structured-data/article

Comment on lines +11 to +17
news: {
title: string;
image: DamImageBlockData;
date: string;
updatedAt: string;
slug: string;
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if we could pick the types from GQLNews here or use a fragment.

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.

4 participants