Search Engine Optimization for Hydrogen
Optimizing Hydrogen for search engines and social media requires configuring the following things:
This guide walks you through the process of setting up each one.
This isn't a comprehensive guide to SEO best practices. Check the Shopify blog for more resources on commerce SEO.
Hydrogen uses Remix's built-in meta
features for SEO tags, and includes the getSeoMeta
utility, which makes it easier and more consistent to render SEO meta tags.
Remix
Check out the Remix docs on meta
exports for more details.
The getSeoMeta
utility accepts SEO metadata about the current route, then renders the corresponding HTML meta tags in your document <head>
:
getSeoMeta example input and output
/app/routes/example.jsx
export const meta = () => {
return getSeoMeta({
title: 'Example title',
});
};
<head>
<!-- ... -->
<title>Example title</title>
<meta property="og:title" content="Example title" />
<meta property="twitter:title" content="Example title" />
<!-- ... -->
</head>
It includes support for titles, descriptions, images, canonical URLs, JSON-LD and more. Check the getSeoMeta reference for a complete list of supported features.
Anchor to Step 1: Import ,[object Object], and use it in your ,[object Object], exportsStep 1: Import getSeoMeta
and use it in your meta
exports
getSeoMeta
and use it in your meta
exportsThe SEO data you want will vary by route. For example, you'll want to render different tags on a product page versus the home page. The following example code shows how the basic pattern applies across all your storefront routes:
Import and use getSeoMeta
/app/routes/root.jsx
import {getSeoMeta} from '@shopify/hydrogen';
// ...
export async function loader() {
return {
//...
// Return an SEO object in your loader data
seo: {
title: "Storefront name",
description: "Storefront description, including relevant keywords to help new customers find your page",
},
};
}
// Pass the loader data to the meta export
export const meta = ({data}) => {
// pass your SEO object to getSeoMeta()
return getSeoMeta(data.seo);
};
import {getSeoMeta} from '@shopify/hydrogen';
import {type MetaFunction} from '@react-router';
// ...
export async function loader({context}: LoaderFunctionArgs) {
return {
//...
// Return an SEO object in your loader data
seo: {
title: "Storefront name",
description: "Storefront description, including relevant keywords to help new customers find your page",
},
};
}
// Pass the loader data to the meta export
export const meta: MetaFunction<typeof loader> = ({data}) => {
// pass your SEO object to getSeoMeta()
return getSeoMeta(data.seo);
};
Anchor to Step 2: Merge SEO data from nested routesStep 2: Merge SEO data from nested routes
Often, two or more nested routes are returning SEO data. In general, you'll want to use the most specific SEO tags available for your route.
For example, if you're on the product page, you want to show a title and description of your product, not the store name and description from the root route.
The following code examples show how to return SEO data from a nested product route, then merge it with SEO data from the root route. By mapping over SEO data returned by all nested routes, The most specific SEO data "wins", overwriting SEO data from higher in the routing structure:
Merge SEO data from nested routes
/app/routes/root.jsx
import {getSeoMeta} from '@shopify/hydrogen';
// ...
export async function loader({context}) {
return {
//...
// Return basic shop SEO data
seo: {
title: "Storefront name",
description: "Storefront description, including relevant keywords to help new customers find your page",
},
};
}
export const meta = ({data}) => {
// Pass your SEO object to getSeoMeta()
return getSeoMeta(data.seo);
};
// Switch to the "Product handle route" to see how these relate
import {getSeoMeta} from '@shopify/hydrogen';
// ...
export async function loader({context}) {
const {product} = await context.storefront.query(/* GraphQL query */);
return {
//...
// Return product SEO data
seo: {
title: product.seo.title || product.title,
description: product.seo.description || product.description,
// ...this could also include image URLs, prices, and more
},
};
}
/// Pass all product loader data to the meta export
export const meta = ({matches}) => {
// Map data from all routes. More specific SEO data from nested routes
// overrides more general SEO data from parent routes.
return getSeoMeta(...matches.map((match) => match.data.seo));
};
Anchor to Step 3 (Optional): Intercept and override route SEO dataStep 3 (Optional): Intercept and override route SEO data
You can implement your own logic inside meta
exports to customize the SEO metadata that each route returns.
As an example, by default Hydrogen removes query parameters from canonical URLs. If you wanted to add them back, the following is an example of how you could do that:
Override SEO data with custom logic
/app/routes/root.jsx
import {getSeoMeta} from '@shopify/hydrogen';
// ...
export const meta = ({data, location}) => {
return getSeoMeta(data.seo).map((meta) => {
if (meta.rel === 'canonical') {
return {
...meta,
// Overwrite the default value of meta.href to append the URL params
href: meta.href + location.search,
};
}
return meta;
});
};
import {getSeoMeta} from '@shopify/hydrogen';
import {type MetaFunction} from '@react-router';
// ...
export const meta: MetaFunction<typeof loader> = ({data, location}) => {
return getSeoMeta(data.seo).map((meta) => {
if (meta.rel === 'canonical') {
return {
...meta,
// Overwrite the default value of meta.href to append the URL params
href: meta.href + location.search,
};
}
return meta;
});
};
Anchor to sitemap.xmlsitemap. xml
Hydrogen's base template includes sitemap.xml
and sitemap.$type.$page.xml
routes by default. If your project doesn't already have these files, then you can generate them with the following Shopify CLI command:
Terminal
By default, the sitemap files are cached for 24 hours.
Anchor to LimitationsLimitations
- When you add or remove pages, the sitemap is automatically updated within one day. Similarly, if you unpublish a product, then the product is removed automatically from the sitemap.
Anchor to robots.txtrobots. txt
Hydrogen's base template includes a robots.txt
route by default. If your project doesn't already have a robots.txt
file, you can generate a new one with the Hydrogen CLI:
Terminal
By default, Hydrogen's robots.txt
file is cached for 24 hours.
Anchor to robots.txt on Oxygenrobots. txt on Oxygen
If you deploy to Oxygen, then your robots.txt
file is only served in your public production environment, and only if you have a custom domain set.
If you make a non-production deployment accessible with a shareable link or an auth bypass token, then Oxygen overrides the deployment's robots.txt
file with a disallow
rule for all bots and crawlers. This prevents exposing content prematurely, as well as potential SEO harm from duplicated content.