A portable, framework-agnostic SEO system for any React or React Native-for-web frontend. Centralizes site metadata in one constants module, derives canonical URLs from a single base, builds per-route metadata (title, description, canonical, Open Graph, Twitter/X cards), generates...
Installation
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A portable, framework-agnostic SEO system for any React or React Native-for-web frontend. Centralizes site metadata in one constants module, derives canonical URLs from a single base, builds per-route metadata (title, description, canonical, Open Graph, Twitter/X cards), generates...
Use this skill when you need a portable, framework-agnostic SEO system for any React or React Native-for-web frontend. Centralizes site metadata in one constants module, derives canonical URLs from a single base, builds per-route metadata (title, description, canonical, Open Graph, Twitter/X cards), generates...
Portable skill — readable by Claude Code, OpenCode, Codex, Cursor, Windsurf, and others.
This skill describes an SEO system — a set of pure builder functions plus a thin
framework adapter — not a component library or a visual style.
It pairs with the frontend-architecture skill: the SEO system lives in a single
service module (services/seo/) and is consumed through one barrel.
The goal: every route ships correct, consistent, machine-readable metadata without
anyone copy-pasting <meta> tags. Site identity lives in one constants module, URLs are
always absolute and canonical, and search engines get a sitemap, robots rules, an RSS
feed, and typed JSON-LD derived from the same content the app already renders.
0. The five core ideas
One source of truth for identity. Site URL, name, description, keywords, author, social handles, OG image, and verification tokens live in a single constants/seo module. Nothing about the site's identity is hardcoded anywhere else.
URLs are always absolute and canonical. A single canonicalUrl(path) function turns any path into an absolute, trailing-slash-normalized URL. Every sitemap entry, RSS link, OG URL, and JSON-LD @id flows through it.
Builders are pure; the adapter is thin. Metadata, sitemap, robots, RSS, and JSON-LD are produced by pure functions that take data and return plain objects. Only one small function touches the framework's metadata type. Pure functions are trivially unit-testable.
Structured data is typed and reused. JSON-LD objects share a JsonLd type and a small set of schema.org builders (Person, WebSite, BlogPosting, CreativeWork, BreadcrumbList, FAQPage). Entities cross-reference each other by stable @id.
Discovery surfaces are generated from content.sitemap.xml, robots.txt, and the RSS feed are built from the same content collections the app renders — never maintained by hand, never drifting.
Everything below is the mechanical application of these five ideas.
1. Directory layout
The SEO system is one service module plus its constants and types. It slots directly into the
frontend-architecture shape (shared/ or services/).
Rule of thumb: builders never import the framework (except the one buildMetadata adapter);
route files never build SEO data inline — they call a builder and mount the result.
2. One source of truth for identity (constants/seo)
Everything about the site's identity is a named constant. No bare strings scattered across
route files, no second copy of the description, no hardcoded base URL.
Why: changing the description or the OG image touches one line. Structured data, OG tags, and
Twitter cards all read the same values, so they can never disagree.
3. Typed data models (types/seo)
Minimal but typed. These are the contracts every builder honors.
Note the I-prefix convention from frontend-architecture applies to stateful UI interfaces;
these SEO data models are plain DTOs and follow the source project's existing convention
(here, unprefixed). Keep whichever convention the host project already uses — consistency wins.
4. Canonical URLs (the spine of the system)
One function, used everywhere. It guarantees absolute, normalized, double-slash-free URLs so
search engines never see two URLs for the same page.
Set the title template, default OG/Twitter cards, robots policy, icons, manifest, and
verification once at the root. Child routes only override what differs.
react-helmet-async (or a head manager) fed by buildMetadata's plain object
build-time script that writes sitemap.xml/robots.txt from the same builders
Expo Router (web)
static head config / expo-router head; SEO matters only on the web target
a small web-only build step calling sitemapEntries/rssItems
For a buildMetadata that must stay framework-neutral, return a plain shape
({ title, description, canonical, ogImage }) and let each adapter translate it — keep the
Next.js Metadata return type only in a Next.js project.
9. Conventions checklist (enforce in review)
All site identity (URL, name, description, keywords, author, handles, OG image, verification) lives in oneconstants/seo module — no duplicates, no hardcoded base URL.
Every absolute URL is produced by canonicalUrl() — no manual SITE_URL + path.
One trailing-slash policy, applied everywhere.
Global metadata (title template, default OG/Twitter, robots, verification, manifest) is set once in the root layout.
Dynamic routes override metadata via buildMetadata (or the framework adapter), passing only what differs.
Every page resolves exactly one canonical; no relative or duplicate canonicals.
sitemap.xml, robots.txt, and the RSS feed are generated from content collections, deduped, all absolute. Private routes are disallowed in robots.
JSON-LD uses the shared structuredData/JsonLd builders; entities cross-reference by stable @id.
Each detail page emits its primary schema (BlogPosting/CreativeWork) + a BreadcrumbList; home emits Person + WebSite.
Builder functions are pure and unit-tested; framework code stays in the thin adapter (route files).
OG image, locale, and Twitter handle are present and consistent across OG + Twitter cards.
Structured data validated with Google's Rich Results Test before release.
10. How to apply this skill
Adding SEO to a site: create constants/seo, types/seo, and services/seo/ (barrel +
structured-data.ts). Wire global metadata in the root layout, then add sitemap, robots, and
feed.xml adapters that mount the builders.
Adding a new content type (e.g. case studies): add its slugs to sitemapEntries, add a
JSON-LD builder if it needs its own schema, and give its detail page a generateMetadata + a
BreadcrumbList.
Debugging duplicate-content / indexing issues: check that every page goes through
canonicalUrl, that the trailing-slash policy is uniform, and that the sitemap contains only
indexable, absolute URLs. Confirm robots isn't blocking what should be indexed.
Reviewing SEO coverage: run the checklist in §9. The highest-value catches are hardcoded URLs
that bypass canonicalUrl (duplicate-content risk) and JSON-LD that duplicates entity fields
instead of referencing a stable @id.
Publishing / installing this skill
This skill follows the Anthropic SKILL.md format and is portable across agents.
Keep it under skills/frontend-seo/SKILL.md in a public GitHub repo.
Keep the frontmatter name and high-signal description — discovery indexes match against it.