Add SEO Support to Item Pages

An item page renders one business-solution item from a parameterized route, such as a product at /product/[handle] or a blog post at /blog/[slug]. Unlike main pages, which get their SEO tags injected automatically with no code, item pages need code to resolve an item's tags.

This article explains how to add the necessary code so SEO tags a Wix user edits in the dashboard appear on your headless frontend. After a one-time layout change to expose a tag slot, you add support in 2 parts:

  1. Register each item-page route so Wix knows it exists.
  2. Render the Wix-resolved tags into the page at request time.

For how these parts fit together, and how main pages differ, see About SEO Support.

Before you begin

Before starting to code, make sure that you have:

  • A Wix-managed headless project built with Astro. It should already include the @wix/astro and @wix/astro-pages packages.

  • @wix/seo and @wix/essentials installed. Use @wix/essentials version 1.0.10 or later:

    Copy
  • Server-rendered item-page routes, configured with output: "server". The calls that fetch SEO tags depend on request context, so don't set prerender = true or use getStaticPaths() on these routes.

Step 1 | Add the SEO tags slot to your layout

Expose a named seo-tags slot in the <head> of your shared layout. Item pages fill this slot with the Wix-resolved tags.

Main pages leave it empty and get their tags from automatic SEO injection, so don't add fallback <title> or <meta name="description"> tags here, since they'd duplicate the injected ones.

Copy

Step 2 | Register the route

In each item-page route file, export a wixMetadata object so the page appears in the registry at /_wix/pages.json. Source the app ID, page identifier, and slug token from WIX_APPS rather than hard-coding them:

Copy

Caution: Reference WIX_APPS directly inside the wixMetadata object, as shown. Don't read it into a separate variable first, such as const { id } = WIX_APPS.<solution>. Because wixMetadata is a module-level export, Astro evaluates it in the module scope, where variables declared in the component body aren't available.

Important: The key inside identifiers must be the route parameter of your route file, not the token the SDK uses. A route file named [handle].astro uses the key handle; [slug].astro uses slug.

Values for each page type

Each SEO-supported page type pairs a page-metadata accessor and slug token from WIX_APPS in @wix/essentials with an item type from seoTags.ItemType in @wix/seo. Because the packages don't reference each other, use this list to pair them for the wixMetadata export in Step 2 and the loadSEOTagsServiceConfig() call in Step 3:

  • Stores product:
    • Page metadata: WIX_APPS.checkoutAndOrders.productPageMetadata
    • Slug token: handle
    • Item type: seoTags.ItemType.STORES_PRODUCT
  • Stores category:
    • Page metadata: WIX_APPS.checkoutAndOrders.categoryPageMetadata
    • Slug token: collection
    • Item type: seoTags.ItemType.STORES_CATEGORY
  • Bookings service:
    • Page metadata: WIX_APPS.bookings.servicePageMetadata
    • Slug token: slug
    • Item type: seoTags.ItemType.BOOKINGS_SERVICE
  • Events page:
    • Page metadata: WIX_APPS.events.eventPageMetadata
    • Slug token: slug
    • Item type: seoTags.ItemType.EVENTS_PAGE
  • Blog post:
    • Page metadata: WIX_APPS.blogs.postPageMetadata
    • Slug token: slug
    • Item type: seoTags.ItemType.BLOG_POST
  • Blog category:
    • Page metadata: WIX_APPS.blogs.categoryPageMetadata
    • Slug token: slug
    • Item type: seoTags.ItemType.BLOG_CATEGORY

The WIX_APPS type is the source of truth for the exact accessor strings, the pageIdentifier each one resolves to, and the current set of supported page types. This list exists to pair each type with its seoTags.ItemType.

Note: For Stores, the accessor is WIX_APPS.checkoutAndOrders, not WIX_APPS.stores. WIX_APPS.stores.id is the catalog ID used for catalogReference.appId in cart operations.

Step 3 | Fetch and render the SEO tags

In the route's frontmatter, import the SEO helpers and call loadSEOTagsServiceConfig() at request time, then render the result into the seo-tags slot. The function takes the following fields:

  • pageUrl: The canonical URL of the current page. Use Astro.url.href.
  • itemType: A seoTags.ItemType value that identifies the page type.
  • itemData: The item's identifier, always as { slug: "<value>" }. The key is literally slug for every page type; the value is your route parameter, such as Astro.params.handle on a [handle].astro route.
Copy

Tip: When the page also fetches item data, run loadSEOTagsServiceConfig() in parallel with that fetch using Promise.all to avoid an extra round trip. See the example below.

loadSEOTagsServiceConfig() returns the tags Wix has already resolved for the item. A Wix user controls these values in the dashboard under SEO & GEO > SEO Settings, where each page type, such as Blog posts, has its own settings, and individual items can override them in their own SEO fields. You don't set these values in code. Your code only renders the resolved result.

Step 4 | Optional: Add structured data

<SEO.Tags> covers the tags managed in the dashboard. To add schema.org structured data for rich results, render a JSON-LD script from the item you already fetched, using the schema type that fits the page, such as Product for store products or Event for events:

Copy

Step 5 | Optional: Update tags during client-side navigation

<SEO.Tags> resolves the tags once, on the server. If your frontend navigates between items on the client without a full page reload, such as moving from one product to another in a single-page flow, the <head> keeps the first item's tags unless you update them.

To re-resolve tags on the client, wrap the relevant content in <SEO.Root>, which provides the SEO service context, and use <SEO.UpdateTagsTrigger>, a render-prop component that exposes an updateSeoTags function. Call it with the new item's type and slug to swap the tags in place:

Copy

<SEO.UpdateTagsTrigger> must be nested inside <SEO.Root>. For a server-rendered page that emits its tags once, <SEO.Tags> alone is enough; you don't need <SEO.Root>.

Step 6 | Verify your setup

To verify that your code is bringing the SEO tags as expected:

  1. Run the build command to build the assets for your project:

    Copy
  2. Run the release command to publish your project:

    Copy
  3. Fetch a real item URL that returns 200, not 404, and inspect the resolved <title> in its <head>:

    Copy

    Confirm the title is that item's real title from the dashboard, not a generic page-type fallback such as Post | <site name>. An unregistered or mistyped route still returns fallback tags, so matching the real item's values is what proves your <SEO.Tags> render resolved the item.

  4. Open /_wix/pages.json and confirm your item-page routes are listed. If the list shows only your static pages, the route isn't registered, so recheck the wixMetadata export. This registry is what feeds the sitemap and the dashboard SEO editor.

  5. Change a value in the dashboard for the relevant page type, and then fetch the page again to confirm the change reaches the live <head>.

Notes:

  • A route registered in /_wix/pages.json but missing <SEO.Tags> still ships your layout's default tags.
  • A page that renders <SEO.Tags> but exports no wixMetadata serves correct tags yet stays invisible to the sitemap.

Examples

Complete examples for each item-page type. Each uses the values from Values for each page type, sources them from WIX_APPS, and runs the item fetch in parallel with loadSEOTagsServiceConfig(). The fetch helpers, such as getProduct, and the <Layout> component stand in for your own code.

Stores product page

Route file: src/pages/product/[handle].astro.

Register the route:

Copy

Fetch and render the tags:

Copy

Stores category page

Route file: src/pages/search/[collection].astro. Category pages resolve tags from the route parameter alone, so there's no item to fetch.

Register the route:

Copy

Fetch and render the tags:

Copy

Bookings service page

Route file: src/pages/bookings/[slug].astro.

Register the route:

Copy

Fetch and render the tags:

Copy

Events page

Route file: src/pages/events/[slug].astro.

Register the route:

Copy

Fetch and render the tags:

Copy

Blog post page

Route file: src/pages/blog/[slug].astro.

Register the route:

Copy

Fetch and render the tags:

Copy

Blog category page

Route file: src/pages/blog-category/[slug].astro. Category pages resolve tags from the route parameter alone, so there's no item to fetch.

Register the route:

Copy

Fetch and render the tags:

Copy

Troubleshooting

The dashboard shows no pages, or /_wix/pages.json is empty

A route file is throwing when Wix imports it, and one failing route is enough to clear the whole list. The usual cause is reading WIX_APPS into a variable instead of referencing it directly inside the wixMetadata export (see Step 2). Fix the export, then rebuild and redeploy.

Item URLs are missing from the sitemap

The route's wixMetadata is missing, or the key in identifiers doesn't match the route's filename parameter: use handle for [handle].astro and slug for [slug].astro. Confirm the route and its identifiers key in /_wix/pages.json.

An item page shows a generic title

loadSEOTagsServiceConfig() didn't resolve the item, so only the fallback tags render. Check that the URL returns 200 rather than 404, that itemType matches the page type, and that itemData.slug is the item's real slug.

Tags don't change during client-side navigation

Server-rendered tags resolve once. To re-resolve them on client navigation, wrap the content in <SEO.Root> and call updateSeoTags with <SEO.UpdateTagsTrigger>, as shown in Step 5.

See also

Last updated: 1 July 2026

Did this help?