Announcements
Deploy the Storefront Next Cartridge for Page Designer
SEO and Metadata
AEO and GEO
UI Styling
Images
Static Assets
CLI Reference
B2C Commerce Release Notes
Ask the Community
Storefront Next template’s built-in SEO features helps optimize your store’s search and product discoverability. The storefront template provides SEO features with hreflang alternate links, canonical URLs, page titles, meta tags, indexing control, and structured data (JSON-LD)
The storefront can render locale-aware <link rel="alternate" hreflang="..."> tags in the document <head>. These tags are generated centrally in src/utils/seo.ts and returned from the root meta export in src/root.tsx.
Search engines use hreflang to understand whether several pages are regional or language-specific versions of the same content.
For an ecommerce storefront, the alternate links feature is especially useful because:
The root loader calls buildSeoMetaDescriptors(...), which returns:
<link rel="canonical"><link rel="alternate" hreflang="..."> per supported localex-default alternate pointing to the site’s default localeThe alternate URLs are built from the current request path plus the configured multisite URL prefix. In practice, this means:
site.supportedLocales.1<!-- When viewing the en-GB version of a page with en-GB (default) and fr-FR locales: -->
2<link rel="canonical" href="https://www.example.com/global/en-GB/product/123" />
3<link rel="alternate" hreflang="en-GB" href="https://www.example.com/global/en-GB/product/123" />
4<!-- self-referencing -->
5<link rel="alternate" hreflang="fr-FR" href="https://www.example.com/global/fr-FR/product/123" />
6<link
7 rel="alternate"
8 hreflang="x-default"
9 href="https://www.example.com/global/en-GB/product/123"
10/>You usually don’t add hreflang tags in route components manually. Update this centralized logic only if you change:
Every page renders a <link rel="canonical"> tag in the <head>. This tells search engines which URL should be treated as the preferred version of the page.
Search engines often discover multiple URLs that show the same page content. Canonical URLs tell them which version should receive SEO credit.
For an ecommerce storefront, canonical URLs are useful because:
The canonical URL is built in src/utils/canonical-url.ts. Storefront Next adds it to the root-level SEO descriptors generated by src/utils/seo.ts and renders it from src/root.tsx.
It applies three normalizations:
?sort=price&q=jacket and ?q=jacket&sort=price produce the same canonical URL./product/jacket/ → /product/jacket).The canonical URL uses an allowlist that specifies which query parameters are kept in canonical URLs. Storefront Next strips any parameter not explicitly listed, such as tracking parameters, analytics IDs, and other non-content parameters.
The current allowlist is defined in src/utils/canonical-url.ts:
| Parameter | Purpose | Used by |
|---|---|---|
q | Search query | Search page |
offset | Pagination offset | Category, Search |
sort | Sort order | Category, Search |
refine | Filter refinements | Category, Search |
pid | Product variant ID | Product detail page |
If you add a feature that uses a new query parameter to change the page’s main content, add it to the allowlist so it’s kept in the canonical URL and in locale alternates.
Example: You add a ?view=grid parameter that switches between grid and list layouts with different product data.
src/utils/canonical-url.tsCONTENT_PARAMS set:1const CONTENT_PARAMS = new Set([
2 "q",
3 "offset",
4 "sort",
5 "refine",
6 "pid",
7 "view", // layout mode (grid/list)
8]);src/utils/canonical-url.test.ts:1it("preserves view param", () => {
2 expect(buildCanonicalUrl(origin, "/category/mens", "?view=grid")).toBe(
3 "https://www.example.com/category/mens?view=grid",
4 );
5});When not to add a parameter: If the parameter doesn’t change the page’s main content, don’t add it to the allowlist. Examples include analytics flags, modal triggers, and UI preferences.
SeoMeta is a React component defined in src/components/seo-meta/index.tsx. Route components render this component to add page-level <title> and <meta> tags. SeoMeta handles page titles, descriptions, robots directives, Open Graph tags, and X (formerly Twitter) Card tags.
Canonical and hreflang links are not rendered by SeoMeta. Those are generated centrally at the root level, as described above.
React hoists these tags into <head> automatically, so you can render the SeoMeta component inside a route component and the final tags still end up in the right place.
The page title and meta tags help search engines understand what a page is about and how it should appear in search results or link previews.
For an ecommerce storefront, these tags are especially useful because:
| Concern | Tags |
|---|---|
| Title | <title> |
| Description | <meta name="description"> |
| Robots | <meta name="robots" content="noindex"> when noIndex is true |
| Open Graph | og:title, og:description, og:type, og:url, og:image, og:site_name |
| X Card / Twitter Card | twitter:card, twitter:title, twitter:description, twitter:image |
1import { SeoMeta } from "@/components/seo-meta";
2
3// `SeoMeta` is a component that you render in your route
4// Standard page — site name is appended automatically
5// Renders: <title>Classic Jacket | My Storefront Next Store</title>
6<SeoMeta
7 title="Classic Jacket"
8 description="A premium leather jacket with a tailored fit."
9 openGraph={{
10 type: "product",
11 url: "https://www.example.com/product/classic-jacket",
12 image: "https://www.example.com/images/classic-jacket.jpg",
13 }}
14/>;| Prop | Type | Default | Description |
|---|---|---|---|
title | string | — | Page title. By default it renders as {title} | {siteName}. If omitted, the site name alone is used. |
rawTitle | boolean | false | Render title exactly as given, with no | {siteName} suffix. See Title Modes below. |
description | string | — | Meta description. Also reused for Open Graph and X Card description tags when those are rendered. |
noIndex | boolean | false | Adds <meta name="robots" content="noindex">. |
siteName | string | t('common:defaultSiteName') | Override the site name used in the title suffix and og:site_name. |
twitter | object | — | X Card metadata (cardType, image). The twitter: meta tag prefix is part of the Card spec and is still used by X. |
openGraph | object | — | Open Graph metadata (type, url, image) for social sharing. When provided without twitter, X Card tags are auto-derived from the Open Graph values. |
SeoMeta supports two title modes through the title and rawTitle props:
| Mode | Props | <title> output | When to use |
|---|---|---|---|
| Suffixed (default) | title="My Page" | My Page | My Storefront Next Store | Most pages: product, category, search, account, and content pages. |
| Raw | rawTitle title="My Page" | My Page | Pages that need full control, for example, the homepage that passes the full store name. |
| Fallback | (no title) | My Storefront Next Store | Only the site name. |
Examples:
1// Suffixed (default) — most pages use this
2<SeoMeta title="Classic Jacket" />
3// → <title>Classic Jacket | My Storefront Next Store</title>
4
5// Raw — homepage or any page needing exact title control
6<SeoMeta rawTitle title="My Storefront Next Store — Shop the Latest" />
7// → <title>My Storefront Next Store — Shop the Latest</title>
8
9// Fallback — no title provided
10<SeoMeta />
11// → <title>My Storefront Next Store</title>title and also contains the localized site name suffix unless rawTitle is set.meta[name="description"] and is reused for Open Graph and X Card descriptions.openGraph prop.twitter prop, or auto-generated from openGraph.noIndex is set for pages that should not be indexed.noIndex: This helps keep low-value or private pages out of search results so search engines focus on pages shoppers should actually land on.When openGraph is provided, SeoMeta renders:
og:titleog:description when description is presentog:type (defaults to website)og:url when providedog:image when providedog:site_nameWhen twitter is provided, or when openGraph is provided without an explicit twitter prop, SeoMeta renders:
twitter:cardtwitter:titletwitter:description when description is presenttwitter:image when an image is availableIf openGraph.image is present and twitter.cardType is not set, the card defaults to summary_large_image; otherwise it defaults to summary.
Open Graph and X Card tags aren’t direct search ranking factors, but they’re still valuable for storefront SEO. When someone shares a storefront URL on a platform such as Facebook, LinkedIn, X, Slack, or other messaging tools, that platform’s crawler usually fetches the page and reads its social metadata.
The crawler uses tags such as:
og:title or twitter:title for the preview headlineog:description or twitter:description for the preview summaryog:image or twitter:image for the preview imagetwitter:card to decide which X card layout to useThis metadata helps the platform build a richer preview card instead of showing a plain URL.
For an ecommerce storefront, this is useful because:
For pages that supply openGraph.url, pass the same absolute URL that you want search engines to treat as canonical. In most routes, this is the canonicalized pageUrl built in the loader.
This keeps search and social signals aligned around the same preferred URL.
The site name appended to page titles (for example, " | My Storefront Next Store") comes from the common.defaultSiteName translation key. To change it, update the value in each locale’s translations.json:
1// src/locales/en-US/translations.json
2{
3 "common": {
4 "defaultSiteName": "Your Store Name"
5 }
6}You can also override it per-route via the siteName prop:
1<SeoMeta title="Sale" siteName="My Outlet Store" />
2// Renders: "Sale | My Outlet Store"The template uses two common patterns depending on whether a page is public or auth-protected.
Public pages: Include both title and description.
1<SeoMeta
2 title={t("meta.title", { defaultValue: "About Us" })}
3 description={t("meta.description", { defaultValue: "Learn more about our story." })}
4/>Auth-protected or transactional pages: Include a title for browser tab UX, but omit the description and set noIndex.
1<SeoMeta title={t("meta.title", { defaultValue: "Order History" })} noIndex />SeoMeta and render it inside your route component:1import { SeoMeta } from "@/components/seo-meta";
2
3export default function MyNewPage() {
4 const { t } = useTranslation("myPage");
5
6 return (
7 <>
8 <SeoMeta
9 title={t("meta.title", { defaultValue: "My Page" })}
10 description={t("meta.description", { defaultValue: "Description of my page." })}
11 />
12 {/* page content */}
13 </>
14 );
15}openGraph and optionally twitter.defaultValue fallbacks. To localize them, add the corresponding keys to your translation files, for example meta.title and meta.description.| Route | Title | Description | Open Graph / Twitter | noIndex |
|---|---|---|---|---|
/ (Home) | Store name (raw) | Welcome message | Yes | — |
/category/:id | Category name | Category page/general description | Yes | — |
/product/:id | Product name | Product page/short description | Yes | — |
/search | Search query | Result count + query | Yes | — |
/about-us | About Us | Store mission description | Yes | — |
/login | Sign In | Sign in prompt | Yes | — |
/cart | Cart | Cart review prompt | Yes | — |
/signup | Sign Up | Account creation prompt | Yes | — |
/checkout | Checkout | — | No | Yes |
/order-confirmation/:orderNo | Order Confirmation | — | No | Yes |
/account/* (all sub-pages) | Page-specific title | — | No | Yes |
Note on search and cart indexability: The template indexes /search and /cart by default. Some storefronts prefer to add noIndex to internal search results (to avoid thin/duplicate content) and to the cart page (session-specific content). Evaluate whether these pages provide value as search landing pages for your store and add noIndex if they do not.
Pages behind authentication (account, wishlist, orders) and transactional pages (checkout, order confirmation) use noIndex because:
If your implementation exposes any of these pages publicly (for example, shared wishlists), remove noIndex from those routes.
Search engines work best when they focus on pages that are useful landing pages for shoppers.
For an ecommerce storefront, noIndex helps by: