Custom Routing with Weaverse
This guide explains how to implement custom URL structures in your Shopify Hydrogen storefront using Weaverse, allowing you to move beyond the standard/products/:handle, /collections/:handle, or /pages/:handle paths provided by default.
Overview
While Hydrogen and React Router 7 provide powerful routing capabilities, you might want URLs that are more SEO-friendly, aligned with marketing campaigns, localized, or simply structured differently from Shopify’s defaults. Examples include:/sale/:campaignName(for a marketing landing page)/fr/produits/:productHandle(for a localized product page)/lookbooks/:collectionHandle(for a themed collection page)/guides/:pageHandle(for content pages using Weaverse’s “Custom Page” feature)
Core Concept: loadPage Drives Content
The key principle is that the content Weaverse renders is determined by the parameters you pass to the context.weaverse.loadPage() function within your route module’s loader, specifically the type and handle arguments.
type: Tells Weaverse what kind of content to load (e.g.,'PRODUCT','COLLECTION','PAGE','CUSTOM'). See Rendering Weaverse Pages for a full list of types.handle: Tells Weaverse which specific instance of that content type to load (e.g., the handle of a specific product or collection).CUSTOMpages are resolved from the request URL, so they need nohandle.
- Creating a route module for your desired URL pattern.
- Registering that module in
app/routes.ts— Pilot uses React Router 7’s manual route registry, so creating a file underapp/routes/does not register a route by itself. - Implementing a
loaderin that module and extracting the identifier (like a handle) from the URL parameters. - Calling
context.weaverse.loadPage()with the explicittypecorresponding to the content you want to display, plus the extractedhandlewhen the type needs one.
Route modules are ordinary React Router 7 modules: import
LoaderFunctionArgs, MetaFunction, and useLoaderData from react-router (not from @remix-run/react or @shopify/remix-oxygen), and return plain objects from loaders instead of json().Relationship to Custom Pages & Templates
- Custom Pages: Custom Routing is often used to provide user-friendly URLs for Custom Pages built in Weaverse. Load those with
loadPage({ type: 'CUSTOM' })— Weaverse resolves the page from the request URL. Reservetype: 'PAGE'with ahandlefor Shopify Page resources, the way Pilot’sapp/routes/pages/regular-page.tsxserves/pages/:pageHandle. - Custom Templates: Custom Routing works seamlessly with Custom Templates. If a resource (like a product) fetched via a custom route has a custom template assigned,
loadPageautomatically uses it. You don’t need extra logic in your custom route’s loader for this.
Implementation Steps
Let’s walk through creating a custom route to display a product page at/item/:productHandle instead of the standard /products/:productHandle.
Step 1: Create the Route Module
Create a new module in your Hydrogen project atapp/routes/items/item.tsx. The file path is just a module location — the URL comes from the registration in the next step.
Step 2: Register the Route in app/routes.ts
Pilot defines its routes explicitly with React Router 7’s route helpers, so a new file under app/routes/ is not reachable until you add it to the registry. Add your pattern inside the :locale? prefix block so the route inherits the storefront’s locale prefix:
:locale?— the optional locale prefix Pilot already applies to storefront routes.item/:productHandle— your custom URL pattern, replacingproducts/:productHandle.- The second argument is the module path relative to
app/.
Step 3: Implement the Loader Function
- Explicit
type: "PRODUCT": This is critical. It tells Weaverse to load the content configured for a product page, even though the URL is/item/.... - Extract
productHandle: Get the handle fromparams. - Fetch Shopify Data: You still need to fetch the corresponding Shopify product data for SEO, potentially for component data binding (though Weaverse handles much of this), and analytics. Use
Promise.allfor efficiency. - Nullable Weaverse data:
loadPage()never rejects on a load failure — it logs and resolves tonull. Atry/catcharound it catches nothing, so branch on the result (or hand it tovalidateWeaverseData). Keeptry/catchfor the operations that really do throw, such asstorefront.query.
Step 4: Render the Weaverse Content
The component itself remains simple, primarily rendering<WeaverseContent /> which uses the weaverseData fetched in the loader.
/item/your-product-handle will render the Weaverse product page associated with your-product-handle.
More Examples
Localized Collection Route
- Desired URL:
/fr/collections-en-vedette/:collectionHandle - Route Module:
app/routes/collections/featured-collection.tsx - Registration:
route("collections-en-vedette/:collectionHandle", "routes/collections/featured-collection.tsx")inside the:locale?block - Loader
loadPageCall:weaverse.loadPage({ type: 'COLLECTION', handle: params.collectionHandle }) - Loader Shopify Data Fetch: Query for the specific collection data using
params.collectionHandle.
Marketing Campaign Page (using Weaverse Custom Page)
- Assumption: You built a “Custom Page” in Weaverse Studio with the handle
summer-sale-2025. - Desired URL:
/summer-sale-2025 - Route Module: already covered by Pilot’s catch-all,
app/routes/catch-all.tsx, registered asroute("*", "routes/catch-all.tsx") - Loader
loadPageCall:weaverse.loadPage({ type: 'CUSTOM' })— the handle comes from the request URL, so nohandleargument - Loader Shopify Data Fetch: Likely none needed unless the page displays specific dynamic products/collections queried separately.
Root-level custom pages need no new route at all: Pilot’s catch-all already resolves any unmatched URL as a
CUSTOM page. Add a dedicated route only when you want a different URL shape (e.g. nesting every campaign under /promo/...), and load it with type: 'CUSTOM' too.Custom Routing with Localization
Custom routes can be combined with Weaverse’s localization features to support multi-locale storefronts with custom URL structures.Explicit Locale in Custom Routes
When implementing custom routes, you can explicitly pass thelocale parameter to loadPage() to ensure the correct localized content is loaded:
Localized Custom URL Patterns
Create locale-specific custom routes with different URL structures per market:Dynamic Locale Selection in Custom Routes
Combine custom routing with dynamic locale detection:Multi-Project Custom Routing
For top-level domain or subdomain-based localization, combine custom routing with multi-project architecture:- Completely different URL structures per market (e.g.,
/vara/for Swedish,/item/for English) - Independent theme settings and page structures per project
- Support for top-level domains (mystore.se, mystore.fr) without complex routing
Important Considerations
- Register Every Route: A module under
app/routes/is unreachable until it appears inapp/routes.ts. If a custom URL 404s or renders your catch-all page, check the registry first. - Choose the Correct
type: Always ensure thetypeparameter inloadPagematches the kind of content you intend to display (PRODUCT, COLLECTION, PAGE, BLOG, ARTICLE, CUSTOM, etc.). UseCUSTOMfor Weaverse custom pages andPAGE+handlefor Shopify Pages. Refer to the Supported Page Types. - Fetch Necessary Shopify Data: If your custom route replaces a standard route (like product or collection), remember to fetch the corresponding Shopify data in your loader for SEO, analytics, or any components that might need it directly.
- Generating Links: Linking to these custom routes from within your application (e.g., in navigation or product grids) will require using the correct path (
/item/...,/fr/collections-en-vedette/...) instead of the default Shopify paths. You might need custom link-building logic or helper functions. - SEO & Sitemaps: Standard sitemap generation might not automatically include these custom routes. You may need to manually add them to your
sitemap.xmllogic. Ensure canonical URLs are correctly set (perhaps pointing to the standard Shopify URL if preferred, or self-referencing the custom URL if it’s the primary one). Consult SEO best practices.
This approach provides significant flexibility in structuring your Hydrogen storefront’s URLs while leveraging Weaverse for visual page building and content management.