SEO and Metadata

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:

  • It helps search engines send shoppers to the right language or locale version of a product, category, or content page.
  • It reduces confusion between similar pages that differ only by locale, currency, or URL prefix.
  • It increases the likelihood that international shoppers land on the version of the storefront that matches their market.

The root loader calls buildSeoMetaDescriptors(...), which returns:

  1. A canonical <link rel="canonical">
  2. One <link rel="alternate" hreflang="..."> per supported locale
  3. An x-default alternate pointing to the site’s default locale

The alternate URLs are built from the current request path plus the configured multisite URL prefix. In practice, this means:

  • Locale-specific URLs are generated automatically from site.supportedLocales.
  • Locale aliases are respected when configured in the multisite setup.
  • The current locale prefix is stripped before alternates are rebuilt, so switching locales keeps the same logical page path.
  • Query parameters follow the same canonical normalization rules described in a section later.
  • Each page includes a self-referencing hreflang tag pointing to itself, which Google requires for valid hreflang sets.

You usually don’t add hreflang tags in route components manually. Update this centralized logic only if you change:

  • The multisite URL prefix format
  • How locale aliases map into URLs
  • Which query parameters are considered content-bearing for canonicalization

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:

  • Product and category pages often collect tracking parameters, sort parameters, and filter parameters that can create many URL variations.
  • It helps consolidate ranking signals instead of spreading them across duplicate URLs.
  • It lowers the risk of duplicate-content problems on search, category, and product detail pages.

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:

  1. Allowlisted query parameters—Only parameters that change page content are kept. Everything else (tracking params, analytics IDs, unknown params) is stripped.
  2. Sorted parameters—Retained params are sorted alphabetically so that ?sort=price&q=jacket and ?q=jacket&sort=price produce the same canonical URL.
  3. Trailing slash removal—Trailing slashes are removed from non-root paths (/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:

ParameterPurposeUsed by
qSearch querySearch page
offsetPagination offsetCategory, Search
sortSort orderCategory, Search
refineFilter refinementsCategory, Search
pidProduct variant IDProduct 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.

  1. Open src/utils/canonical-url.ts
  2. Add the parameter to the CONTENT_PARAMS set:
  1. Add a corresponding test in src/utils/canonical-url.test.ts:

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:

  • Clear titles help product, category, and content pages compete for relevant searches.
  • Good descriptions can improve click-through rate from search results.
  • Consistent metadata makes it easier for search engines to understand the difference between product, category, cart, account, and content pages.
ConcernTags
Title<title>
Description<meta name="description">
Robots<meta name="robots" content="noindex"> when noIndex is true
Open Graphog:title, og:description, og:type, og:url, og:image, og:site_name
X Card / Twitter Cardtwitter:card, twitter:title, twitter:description, twitter:image
PropTypeDefaultDescription
titlestringPage title. By default it renders as {title} | {siteName}. If omitted, the site name alone is used.
rawTitlebooleanfalseRender title exactly as given, with no | {siteName} suffix. See Title Modes below.
descriptionstringMeta description. Also reused for Open Graph and X Card description tags when those are rendered.
noIndexbooleanfalseAdds <meta name="robots" content="noindex">.
siteNamestringt('common:defaultSiteName')Override the site name used in the title suffix and og:site_name.
twitterobjectX Card metadata (cardType, image). The twitter: meta tag prefix is part of the Card spec and is still used by X.
openGraphobjectOpen 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:

ModeProps<title> outputWhen to use
Suffixed (default)title="My Page"My Page | My Storefront Next StoreMost pages: product, category, search, account, and content pages.
RawrawTitle title="My Page"My PagePages that need full control, for example, the homepage that passes the full store name.
Fallback(no title)My Storefront Next StoreOnly the site name.

Examples:

  • Title comes from title and also contains the localized site name suffix unless rawTitle is set.
  • Description renders meta[name="description"] and is reused for Open Graph and X Card descriptions.
  • Open Graph is opt-in via the openGraph prop.
  • X Card / Twitter Card is opt-in via the twitter prop, or auto-generated from openGraph.
  • Robots only renders when noIndex is set for pages that should not be indexed.
  • Title: Search engines use the page title as a strong signal for what the page is about, and it is often shown as the main clickable headline in search results.
  • Description: Search engines may use the meta description as the summary snippet in search results, which can help attract clicks.
  • Open Graph: Social platforms use these tags when a page is shared, helping product and content links look more complete and trustworthy.
  • X Card / Twitter Card: Similar to Open Graph, these tags improve how shared links appear on X.
  • Robots / 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:title
  • og:description when description is present
  • og:type (defaults to website)
  • og:url when provided
  • og:image when provided
  • og:site_name

When twitter is provided, or when openGraph is provided without an explicit twitter prop, SeoMeta renders:

  • twitter:card
  • twitter:title
  • twitter:description when description is present
  • twitter:image when an image is available

If 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 headline
  • og:description or twitter:description for the preview summary
  • og:image or twitter:image for the preview image
  • twitter:card to decide which X card layout to use

This metadata helps the platform build a richer preview card instead of showing a plain URL.

For an ecommerce storefront, this is useful because:

  • Product, category, and campaign links look more polished and trustworthy when shared.
  • Preview cards can show the product name, a useful summary, and a strong product image before the shopper even clicks.
  • Better-looking previews can improve click-through from social, messaging, affiliate, and marketing channels.
  • Consistent preview data reduces the chance that a platform picks incomplete or low-quality fallback content from the page.

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:

You can also override it per-route via the siteName prop:

The template uses two common patterns depending on whether a page is public or auth-protected.

Public pages: Include both title and description.

Auth-protected or transactional pages: Include a title for browser tab UX, but omit the description and set noIndex.

  1. Import SeoMeta and render it inside your route component:
  1. If the page should be shared socially, pass openGraph and optionally twitter.
  2. All title and description strings use i18n with defaultValue fallbacks. To localize them, add the corresponding keys to your translation files, for example meta.title and meta.description.
RouteTitleDescriptionOpen Graph / TwitternoIndex
/ (Home)Store name (raw)Welcome messageYes
/category/:idCategory nameCategory page/general descriptionYes
/product/:idProduct nameProduct page/short descriptionYes
/searchSearch queryResult count + queryYes
/about-usAbout UsStore mission descriptionYes
/loginSign InSign in promptYes
/cartCartCart review promptYes
/signupSign UpAccount creation promptYes
/checkoutCheckoutNoYes
/order-confirmation/:orderNoOrder ConfirmationNoYes
/account/* (all sub-pages)Page-specific titleNoYes

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:

  • Account pages aren’t useful landing pages for search: Crawlers trying to visit account, wishlist, or order pages will usually be redirected to the login page instead of seeing the actual account content.
  • Checkout pages are often empty without shopper state: A crawler visiting checkout without an active basket will usually see an empty or unusable checkout experience, which is not helpful in search results.
  • Some pages are session-specific: An order confirmation page or another shopper-specific page has no value for a general search audience.

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:

  • Keeping checkout, account, and other private flows out of search results
  • Preventing thin, empty, or session-specific pages from being indexed
  • Helping search engines spend more attention on high-value pages such as product, category, brand, and content pages