Skip to main content

Page SEO

Weaverse Studio lets merchants edit per-page SEO metadata (title, description, canonical, robots, Open Graph, Twitter card, JSON-LD) on every Weaverse page. To render those tags on the storefront, your theme needs to forward weaverseData to a single helper — getWeaverseSeoMeta — from a meta export. This guide explains where to wire the helper, how it interacts with code-defined SEO, and how to clean up any legacy override layer. It is written so you can apply it to any Hydrogen theme without prior knowledge of the file layout.
What changes on the storefront? The routes you opt in export meta that returns the SEO tags configured in Studio. Precedence is per route, not global: routes whose SEO is already derived from Shopify data (products, collections, Shopify pages) keep their code-defined SEO, and — in Pilot — so does the real homepage (INDEX). Pages without SEO configured in Studio fall through silently (no overrides).

Prerequisites

  • @weaverse/hydrogen >= 5.14.0 (the version that introduced getWeaverseSeoMeta).
  • A Hydrogen route that loads a Weaverse page via context.weaverse.loadPage(...).
If your theme is on an older SDK, bump it:

TL;DR

In a route that should take its SEO from Studio (a CUSTOM page route, for example), export meta like this:
getWeaverseSeoMeta reads the SEO record embedded in the loaded page and returns the meta-tag array that React Router’s meta export expects. If the page has no SEO configured, it returns a minimal robots: index,follow descriptor — safe to call unconditionally. The rest of this guide covers three real-world patterns and the cleanup steps you typically need.

Where to wire it

You decide per route whether Studio-edited SEO should apply. The routes that benefit most are the ones rendering Weaverse CUSTOM pages: the catch-all that serves custom pages, and the home/index route when it also serves root-level custom handles. Template routes (product, collection, Shopify page) are an optional supplement — they normally keep their Shopify-derived SEO. The recipes below are independent of each other; the route file determines which one applies.

Catch-all route — CUSTOM pages

Most themes have a catch-all route that resolves any unmatched URL into a Weaverse CUSTOM page. Typical filenames: routes/catch-all.tsx, routes/($locale).$.tsx, routes/$.tsx. If your catch-all currently has no meta export, add one. Before
After

Home / index route — INDEX (and root-level CUSTOM)

Many themes serve both the real homepage (type: "INDEX") and root-level CUSTOM handles from the same index route. Pilot’s app/routes/home.tsx splits the two: the real homepage keeps its code-defined SEO (seoPayload.home({ shop }), which reflects the shop name), while root-level CUSTOM handles served by the same route get their SEO from Weaverse. Loader — compute seo only for INDEX, leave it null for CUSTOM, and return both weaverseData and seo:
Meta — code-defined SEO when it exists (INDEX), Weaverse SEO otherwise (CUSTOM):
Want merchants to edit homepage SEO from Studio too? That is a deliberate theme decision, not the default. Invert the branch — prefer getWeaverseSeoMeta(data?.weaverseData) when the merchant has filled in a title or description in Studio, and fall back to seoPayload.home({ shop }) otherwise — so the homepage never ships with empty meta tags. Note the tradeoff: whichever way you choose, tell your merchants, because a field they edit in Studio for a route that keeps code-defined SEO will silently have no effect on the storefront.

Template routes — optional supplement (product, collection, blog, page)

Routes for Shopify resources (product, collection, blog, article, Shopify pages) typically build their SEO from the Shopify payload via seoPayload.product(...), seoPayload.collection(...), etc. Leave those as-is. For templates, Shopify-driven SEO already covers the resource-specific fields (including structured JSON-LD with price, availability, ratings, etc.) that Weaverse Studio does not surface today.
If you want Weaverse SEO to supplement template routes: Add getWeaverseSeoMeta to the template’s meta export and append it after the Shopify tags, not replace them. Avoid concatenating arrays naively — duplicate <meta name="description"> tags can confuse crawlers. The safest pattern is to let Shopify SEO win and only pull in Weaverse tags that are absent from the Shopify payload (e.g. a custom robots override or extra OG fields).

Step-by-step: applying this to a theme

  1. Bump the SDK to @weaverse/hydrogen@^5.14.0 and reinstall.
  2. Locate every route that calls context.weaverse.loadPage(...) — grep -rln "weaverse.loadPage" app/routes.
  3. For each, decide which recipe applies (catch-all, home/index, or — optionally — template) and apply it. Routes that already build SEO from Shopify data stay as they are.
  4. Remove legacy overrides (see next section), if any.
  5. Run typecheck and build. Fix any errorComponent regression caused by the SDK bump (see SDK 5.14 side effect).
  6. Smoke test: open a Weaverse page in Studio, set a Title and Description, save, view source on the storefront URL. The tags should appear.

Removing a legacy override layer

Some themes shipped a hardcoded SEO override config to brand custom pages before Studio could edit SEO. Typical signatures:
  • A file like app/.server/seo-overrides.ts, app/utils/seo-overrides.ts, or similar, with a hardcoded Record<string, { title, description }>.
  • A helper like seoPayload.customPage({ url }) that maps the URL to one of those overrides.
  • A route that does seoPayload.customPage({ url: request.url }) and returns it in the loader.
Once Studio-edited SEO works, this layer is dead weight and competes with Weaverse:
  1. Delete the override file (e.g. seo-overrides.ts).
  2. Remove customPage from your SEO payload module — both the function and its export.
  3. Update routes that called seoPayload.customPage({ url: request.url }) — replace with the meta export pattern above. The loader no longer needs request for SEO purposes.
Why remove it? Studio-edited SEO is per-page and merchant-controlled. A static override would silently shadow whatever the merchant configures in Studio.

SDK 5-14 error component prop type

@weaverse/hydrogen 5.14 narrowed WeaverseHydrogenRoot’s errorComponent prop to React.FC<{ error: unknown }>. If your theme passes a custom error component typed as { error?: { message; stack? } }, TypeScript will now complain. The fix is to accept unknown and narrow at runtime:
Apply the same narrowing pattern wherever you read .stack or other fields off the error.

Verification checklist

  • @weaverse/hydrogen is at ^5.14.0 (or newer) in package.json and the installed node_modules version matches.
  • Every route you opted in exports a meta function returning getWeaverseSeoMeta(data?.weaverseData) (or branches per the home/index recipe).
  • Any legacy seo-overrides.ts / customPage helper is deleted.
  • typecheck passes (or only flags pre-existing, unrelated errors).
  • build succeeds.
  • Setting a Title/Description in Studio is reflected in the storefront’s <head> for that page.