Skip to main content

Page Metadata

The theme owns the title, the description, the canonical link, and the social tags of every page. The engine injects Helmet into theme scope, so you can import it without installing it.

Read SEO Overview first for the split between platform and theme.

Set metadata on a page

import React from "react";
import { Helmet } from "react-helmet-async";
import { useGlobalStore } from "fdk-core/utils";

function ProductDescriptionPage({ fpi }) {
const product = useGlobalStore(fpi.getters.PRODUCT_DETAILS);

const title = product?.seo?.title || product?.name;
const description = product?.seo?.description || product?.short_description;
const image = product?.medias?.[0]?.url;

return (
<>
<Helmet>
<title>{title}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonicalUrl} />

<meta property="og:type" content="product" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
{image && <meta property="og:image" content={image} />}

<meta name="twitter:card" content="summary_large_image" />
</Helmet>

{/* page content */}
</>
);
}

Rules

Prefer the SEO values from the API. Most catalog entities carry a seo object with a title and a description that the merchant controls. Use it, and fall back to the entity name only when it is empty. A theme that always builds its own title takes that control away from the merchant.

Set metadata during the first render, not in an effect. Metadata added after mount is not in the server HTML. A crawler that does not run the page scripts never sees it.

// Wrong. The server HTML has no title.
useEffect(() => {
document.title = product.name;
}, [product]);

// Correct. The title is in the server HTML.
<Helmet><title>{product.name}</title></Helmet>

Give every page a canonical link. A storefront reaches the same product through several paths, including filter and tracking query parameters. Without a canonical, those become duplicate pages. Build the canonical from the slug of the entity, and leave query parameters out of it.

Do not add a JSON-LD script. The engine already emits structured data for the page type. A second graph conflicts with it. Read SEO Overview.

Keep one <h1> per page, and let the heading levels descend in order.

Write real alt text on content images. Leave alt empty only for decoration.

Verification

  1. Open the page and use View Source, not the element inspector. The inspector shows the state after hydration; the source shows what a crawler receives.
  2. Confirm the title, the description and the canonical are present in that source.
  3. Confirm the main content text is present in the source.
  4. Confirm exactly one JSON-LD block exists. Two means the theme is adding its own.
  5. Check a product, a listing, a custom page and the home page. Each has a different resolver path.

Was this section helpful?