Skip to content

Add JSON-LD and llms.txt for AI search - #782

Merged
exploIF merged 8 commits into
software-mansion:mainfrom
halskiszymon:geo-structured-data
Aug 24, 2026
Merged

Add JSON-LD and llms.txt for AI search#782
exploIF merged 8 commits into
software-mansion:mainfrom
halskiszymon:geo-structured-data

Conversation

@halskiszymon

Copy link
Copy Markdown
Collaborator

Two SEO changes for the docs site. Nothing changes the rendered page.

  • JSON-LD: Organization with the same @id as swmansion.com, plus SoftwareSourceCode for the library, derived from siteConfig. The identical @id is what makes engines read the docs and the company site as one entity instead of two unrelated ones.
  • llms.txt, generated at build — no new dependency. Lists every docs page. The docs build now fails if the file is missing or lists nothing.
  • docs/static/robots.txt deleted. It never did anything: robots.txt is only read from the origin root, and this one sat at /react-native-enriched-html/robots.txt. Its rules now live at the origin (Add robots.txt for the docs.swmansion.com origin software-mansion.github.io#1, merged), where they actually apply.

Both come from one local plugin, plugins/swm-geo.js, registered with a single line. The same change is going out across the docs repos; reference: software-mansion/react-native-reanimated#10332.

Check: CI prints llms.txt lists N pages.

One local plugin injects Organization and SoftwareSourceCode structured data
using the same @id as swmansion.com, and writes llms.txt from the pages
Docusaurus just built. CI fails if the file is missing or lists nothing.
A title containing regex metacharacters would change the pattern's meaning or
throw at build time.
@halskiszymon
halskiszymon marked this pull request as ready for review August 20, 2026 09:31
Copilot AI lite review requested due to automatic review settings August 20, 2026 09:31

Copilot AI left a comment

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.

Pull request overview

This PR adds SEO-oriented outputs to the Docusaurus docs build by introducing a local plugin that (1) injects JSON-LD structured data into the generated HTML and (2) generates an llms.txt index of docs pages during postBuild. It also removes an ineffective robots.txt that wasn’t being served from the origin root, and adds CI enforcement that llms.txt is generated and non-empty.

Changes:

  • Add docs/plugins/swm-geo.js Docusaurus plugin to inject JSON-LD and generate build/llms.txt.
  • Register the plugin in docs/docusaurus.config.js.
  • Remove docs/static/robots.txt and add a CI step validating build/llms.txt presence/content.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
docs/static/robots.txt Removes a non-origin robots.txt that wouldn’t apply at the site root.
docs/plugins/swm-geo.js Adds JSON-LD injection and build-time llms.txt generation from built routes/pages.
docs/docusaurus.config.js Registers the new local Docusaurus plugin.
.github/workflows/docs-build.yml Enforces llms.txt existence and that it lists at least one page.
Suppressed comments (2)

docs/plugins/swm-geo.js:83

  • Because routeBasePath is '/', most docs routes won’t match SECTIONS[...] and will fall back to Pages. If the intent is for docs to be grouped as "Documentation", use that as the default section and only override for known non-doc sections (e.g. blog/examples).

    const section = SECTIONS[relative.split('/')[0]] ?? 'Pages';
    const line = `- [${page.title}](${url.replace(/\/$/, '')}${route})${page.description ? `: ${page.description}` : ''}`;

docs/plugins/swm-geo.js:93

  • If docs routes are treated as "Documentation" by default (see section selection above), the 'Pages' section label becomes misleading/unreachable for this site. Consider removing it from the emitted section order to keep the llms.txt structure aligned with actual grouping.

  for (const section of ['Documentation', 'Examples', 'Blog', 'Pages']) {
    const entries = grouped.get(section);

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/plugins/swm-geo.js
Comment thread docs/plugins/swm-geo.js
Comment thread docs/plugins/swm-geo.js Outdated
Comment thread docs/plugins/swm-geo.js
This site sets routeBasePath: '/', so route segments are `fundamentals`,
`guides`, `api-reference` and never `docs` — the SECTIONS map never
matched and every page fell into the generic `Pages` group. Sections are
now derived from the page's folder when no explicit key matches, which
also covers `examples` and `blog` not existing here.

JSON.stringify leaves `<` unescaped, so a `</script>` inside any config
value would close the tag early. Escaped to <.

@szydlovsky szydlovsky left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks ok if we resolve all the comments above

@exploIF
exploIF self-requested a review August 21, 2026 10:40
Comment thread docs/plugins/swm-geo.js
`blog` and `examples` title-case to exactly the strings the map returned,
so only `docs` -> `Documentation` was ever a real rename. Section names
now come from the folder a page lives in, with that one exception.

Output is unchanged for every path shape: docs/ still gives Documentation,
blog/ still gives Blog, examples/ still gives Examples.
@exploIF
exploIF merged commit 505a055 into software-mansion:main Aug 24, 2026
9 checks passed
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