Markets in Hydrogen
This recipe shows how to add support for Shopify Markets to your Hydrogen app. Markets let you segment your audience based on location and serve different content to each market.
You can use Markets in a variety of ways. In this recipe, you'll set up basic localization support for your Hydrogen store, learn what options are available for routing, add a country selector component to your app, and set up links that work across localized versions of your store.
There are several ways to implement localization in your Shopify Hydrogen store, and the approach you take will depend on your project's requirements. This recipe uses URL-based localization, which makes market information visible in the URL. This provides two key benefits:
- It's transparent to search engine crawlers.
- It allows each localized version of your store to be properly indexed.
This approach is typically implemented in two ways:
- Path-based localization (recommended)
- Example:
example.com/fr-ca/products - Implementation: Requires adding a locale parameter to your routes
- Rename
routes/_index.tsxtoroutes/($locale)._index.tsx
- Rename
- Advantages: No infrastructure changes needed
- Considerations: Requires additional code to handle link formatting throughout your application
- Example:
- Subdomain or top-level domain localization
- Example:
fr-ca.example.com/products(orexample.fr/products) - Implementation: Requires infrastructure configuration
- Advantages: Maintains consistent URL structure across localized stores
- Considerations: More complex setup at the infrastructure level
- Example:
Although you can use other methods for localization (like cookies or HTTP headers), these approaches have one significant disadvantage: they're not visible to search engine crawlers. This can negatively impact your SEO for different markets.
In this recipe, we'll implement path-based localization.
This recipe is particularly useful for existing Hydrogen projects. If you need to set up a brand new Hydrogen app, you can get a solid foundation by selecting the localization options when setting up your new project using the Shopify CLI. You can also use h2 setup markets to add localization support to your new Hydrogen app.
This recipe is particularly useful for existing Hydrogen projects. If you need to set up a brand new Hydrogen app, you can get a solid foundation by selecting the localization options when setting up your new project using the Shopify CLI. You can also use h2 setup markets to add localization support to your new Hydrogen app.
Anchor to RequirementsRequirements
- Set up your store's regions and languages using Shopify Markets.
- Configure your products appropriately for each market.
- Make sure your Hydrogen app is configured to use a default
languageandcountry code. They will be used as the fallback when no market is explicitly selected.
Anchor to IngredientsIngredients
New files added to the template by this recipe.
| File | Description |
|---|---|
| app/components/CountrySelector.tsx | A component that displays a country selector inside the Header. |
| app/components/Link.tsx | A unified locale-aware Link component that handles both regular links and navigation links with active states. Automatically prepends locale prefixes and cleans menu URLs. |
| app/lib/i18n.ts | Comprehensive i18n utilities including locale detection, path transformation hooks, URL cleaning functions, and locale validation. Centralizes all localization logic in one place. |
| app/routes/($locale)._index.tsx | A route that renders a localized version of the home page. |
| app/routes/($locale).account.$.tsx | Fallback route for unauthenticated account pages with locale support |
| app/routes/($locale).account._index.tsx | Localized account dashboard redirect route |
| app/routes/($locale).account.addresses.tsx | Customer address management page with locale-aware forms and links |
| app/routes/($locale).account.orders.$id.tsx | Individual order details page with localized currency and date formatting |
| app/routes/($locale).account.orders._index.tsx | Customer order history listing with locale-specific pagination |
| app/routes/($locale).account.profile.tsx | Customer profile editing form with localized field labels |
| app/routes/($locale).account.tsx | Account layout wrapper with locale-aware navigation tabs |
| app/routes/($locale).account_.authorize.tsx | OAuth authorization callback route with locale preservation |
| app/routes/($locale).account_.login.tsx | Customer login redirect with locale-specific return URL |
| app/routes/($locale).account_.logout.tsx | Logout handler that maintains locale after sign out |
| app/routes/($locale).blogs.$blogHandle.$articleHandle.tsx | Blog article page with locale-specific content and SEO metadata |
| app/routes/($locale).blogs.$blogHandle._index.tsx | Blog listing page with localized article previews and pagination |
| app/routes/($locale).blogs._index.tsx | All blogs overview page with locale-aware navigation links |
| app/routes/($locale).cart.tsx | A localized cart route. |
| app/routes/($locale).collections.$handle.tsx | Collection page displaying products with locale-specific pricing and availability |
| app/routes/($locale).collections._index.tsx | Collections listing page with localized collection names and images |
| app/routes/($locale).collections.all.tsx | All products page with locale-based filtering and sorting |
| app/routes/($locale).pages.$handle.tsx | Dynamic page route for locale-specific content pages |
| app/routes/($locale).policies.$handle.tsx | Policy page (privacy, terms, etc.) with locale-specific legal content |
| app/routes/($locale).policies._index.tsx | Policies index page listing all available store policies |
| app/routes/($locale).products.$handle.tsx | A route that renders a localized version of the product page. |
| app/routes/($locale).search.tsx | Search results page with locale-aware product matching and predictive search |
| app/routes/($locale).tsx | A utility route that makes sure the locale is valid. |
Anchor to Step 1: Add localization utilities and update core componentsStep 1: Add localization utilities and update core components
In this section, we'll create utilities to handle localization and country selection, and update the core components to use these utilities.
Anchor to Step 1.1: Update CartLineItem with locale-aware product linksStep 1. 1: Update Cart Line Item with locale-aware product links
Update cart line items to use the unified Link component for product links.
Create a new CountrySelector component that allows users to select the locale from a dropdown of the supported locales.
To handle redirects, use a Form that updates the cart buyer identity,
which eventually redirects to the localized root of the app.
File
Anchor to Step 1.3: Create a unified locale-aware Link componentStep 1. 3: Create a unified locale-aware Link component
Create a single Link component that handles both regular links and navigation links. This component automatically:
- Prepends the current locale to paths
- Supports variant="nav" for navigation links with active states
- Cleans invalid locale prefixes from menu URLs
- Enables locale switching while preserving paths
File
Anchor to Step 1.4: Create comprehensive i18n utilitiesStep 1. 4: Create comprehensive i18n utilities
Create a centralized i18n module that includes:
- The
useSelectedLocale()hook to get the current locale from route data - The
useLocalizedPath()hook for intelligent path transformation - The
cleanPath()function to remove invalid locale/language prefixes - The
findLocaleByPrefix()function to detect locales in paths - The
normalizePrefix()function for consistent prefix formatting - Locale validation utilities for route params
- Support for case-insensitive locale matching
File
Anchor to Step 1.5: Update ProductItem to use locale-aware LinkStep 1. 5: Update Product Item to use locale-aware Link
Replace standard react-router Link imports with the new unified Link component. This ensures all product links automatically include the correct locale prefix.
Anchor to Step 1.6: Add the selected locale to the contextStep 1. 6: Add the selected locale to the context
Detect the locale from the URL path, and add it to the HydrogenContext.
Anchor to Step 1.7: Update Header with CountrySelector and locale-aware LinksStep 1. 7: Update Header with Country Selector and locale-aware Links
- Add the
CountrySelectorcomponent to the header navigation. - Update all navigation links to use the unified
Linkcomponent withvariant="nav".
Menu URLs are automatically cleaned of invalid locale prefixes.
File
Anchor to Step 1.8: Add the selected locale to the root routeStep 1. 8: Add the selected locale to the root route
- Include the selected locale in the root route's loader data.
- Make sure to redirect to the 404 page if the requested locale is not supported.
- Add a key prop to the
PageLayoutcomponent to make sure it re-renders when the locale changes.
Anchor to Step 2: Localizing the individual routesStep 2: Localizing the individual routes
In this section, we'll add localization to the individual routes using the language dynamic segment.
Anchor to Step 2.1: Update CartMain with locale-aware linksStep 2. 1: Update Cart Main with locale-aware links
Replace all Link imports to use the unified locale-aware Link component for consistent navigation.
Anchor to Step 2.2: Add language dynamic segment to the desired routesStep 2. 2: Add language dynamic segment to the desired routes
To implement path-based localization, add a language
dynamic segment to your localized routes (for example, renaming routes/_index.tsx
to routes/($locale)._index.tsx).
For brevity, we'll focus on the home page, the cart page, and the product page in this example. In your app, you should do this for all the app routes.
Anchor to Step 2.3: Add localization to the home pageStep 2. 3: Add localization to the home page
- Add the dynamic segment to the home page route.
- Use the new
Linkcomponent as a drop-in replacement.
Rename app/routes/_index.tsx to app/routes/($locale)._index.tsx.
Rename app/routes/_index.tsx to app/routes/($locale)._index.tsx.
File
Anchor to Step 2.4: Add localization to the cart pageStep 2. 4: Add localization to the cart page
Add the dynamic segment to the cart page route.
Rename app/routes/cart.tsx to app/routes/($locale).cart.tsx.
Rename app/routes/cart.tsx to app/routes/($locale).cart.tsx.
File
Anchor to Step 2.5: Add localization to the product pageStep 2. 5: Add localization to the product page
- Add the dynamic segment to the product page route.
- Update the
metafunction to also update the canonical URL to use the localized prefix.
Rename app/routes/products.$handle.tsx to app/routes/($locale).products.$handle.tsx.
Rename app/routes/products.$handle.tsx to app/routes/($locale).products.$handle.tsx.
File
Anchor to Step 2.6: Add a utility route to validate the locale.Step 2. 6: Add a utility route to validate the locale.
Add a utility route in $(locale).tsx that will use localeMatchesPrefix
to validate the locale from the URL params. If the locale is invalid,
the route will throw a 404 error.
File
Anchor to Step 2.7: Handle unauthenticated account pagesStep 2. 7: Handle unauthenticated account pages
Add a fallback route for unauthenticated account pages with locale support.
File
Anchor to Step 2.8: Redirect to account dashboardStep 2. 8: Redirect to account dashboard
Add a localized account dashboard redirect route.
File
Anchor to Step 2.9: Add address managementStep 2. 9: Add address management
Add a customer address management page with locale-aware forms and links.
File
Anchor to Step 2.10: Show order detailsStep 2. 10: Show order details
Add an individual order details page with localized currency and date formatting.
File
Anchor to Step 2.11: Display order historyStep 2. 11: Display order history
Implement customer order history listing with locale-specific pagination.
File
Anchor to Step 2.12: Build customer profile pageStep 2. 12: Build customer profile page
Add a customer profile editing form with localized field labels.
File
Anchor to Step 2.13: Create account layoutStep 2. 13: Create account layout
Add an account layout wrapper with locale-aware navigation tabs.
File
Add an OAuth authorization callback route with locale preservation.
File
Anchor to Step 2.15: Create login redirectStep 2. 15: Create login redirect
Add a customer login redirect with a locale-specific return URL.
File
Anchor to Step 2.16: Handle logoutStep 2. 16: Handle logout
Add a logout handler that maintains locale after the user signs out.
File
Anchor to Step 2.17: Show blog articlesStep 2. 17: Show blog articles
Add a blog article page with locale-specific content and SEO metadata.
File
Anchor to Step 2.18: List blog postsStep 2. 18: List blog posts
Add a blog listing page with localized article previews and pagination.
File
Anchor to Step 2.19: Display all blogsStep 2. 19: Display all blogs
Add an overview page for all blogs with locale-aware navigation links.
File
Anchor to Step 2.20: Show collection pagesStep 2. 20: Show collection pages
Add a collection page displaying products with locale-specific pricing and availability.
File
Anchor to Step 2.21: List all collectionsStep 2. 21: List all collections
Add a collections listing page with localized collection names and images.
File
Anchor to Step 2.22: Show all productsStep 2. 22: Show all products
Add an "All products" page with locale-based filtering and sorting.
File
Anchor to Step 2.23: Display content pagesStep 2. 23: Display content pages
Add a dynamic page route for locale-specific content pages.
File
Anchor to Step 2.24: Show policy pagesStep 2. 24: Show policy pages
Add a policy page (privacy, terms, etc.) with locale-specific legal content.
File
Anchor to Step 2.25: List all policiesStep 2. 25: List all policies
Add a policies index page that lists all available store policies.
File
Anchor to Step 2.26: Build search functionalityStep 2. 26: Build search functionality
Add a search results page with locale-aware product matching and predictive search.
File
Anchor to Deleted FilesDeleted Files
- templates/skeleton/app/routes/account.$.tsx
- templates/skeleton/app/routes/account._index.tsx
- templates/skeleton/app/routes/account.addresses.tsx
- templates/skeleton/app/routes/account.orders.$id.tsx
- templates/skeleton/app/routes/account.orders._index.tsx
- templates/skeleton/app/routes/account.profile.tsx
- templates/skeleton/app/routes/account.tsx
- templates/skeleton/app/routes/account_.authorize.tsx
- templates/skeleton/app/routes/account_.login.tsx
- templates/skeleton/app/routes/account_.logout.tsx
- templates/skeleton/app/routes/blogs.$blogHandle.$articleHandle.tsx
- templates/skeleton/app/routes/blogs.$blogHandle._index.tsx
- templates/skeleton/app/routes/blogs._index.tsx
- templates/skeleton/app/routes/collections.$handle.tsx
- templates/skeleton/app/routes/collections._index.tsx
- templates/skeleton/app/routes/collections.all.tsx
- templates/skeleton/app/routes/pages.$handle.tsx
- templates/skeleton/app/routes/policies.$handle.tsx
- templates/skeleton/app/routes/policies._index.tsx
- templates/skeleton/app/routes/search.tsx
Anchor to Next stepsNext steps
- Test your implementation by going to your store and selecting a different market from the country selector.
- Refer to the Shopify Help Center for more information on how to optimize and manage your international markets.