Add Hydrogen to an existing project
You don't need a starter template to use Hydrogen. You can add it to a new or existing project that uses any framework that runs server code. This guide shows you how to install Hydrogen, connect it to a store, wire it into your app's request lifecycle, and show your store's products on a page.
If you want a storefront that's already built, then start from a starter template instead. Refer to Deploy a template.
Anchor to What you'll learnWhat you'll learn
In this guide, you'll learn how to do the following tasks:
- Install Hydrogen in a project that uses the framework of your choice.
- Configure environment variables for mock.shop or for your own store.
- Wire the Storefront API client, request handlers, and customer accounts into your app.
- Show your store's products on a page, with a link to buy each one.
- Check that your storefront works.
Anchor to RequirementsRequirements
- Install Node.js version 22 or 24.
- Use a JavaScript project with a
package.jsonfile at its root, built with a framework that runs server code, like route handlers, middleware, loaders, or server functions. Hydrogen runs before your router on each request, so apps that are browser-only or configured only for static output don't work. - To connect your own store, get Storefront API access through the Headless channel or the Hydrogen channel. You don't need a store to start building with mock.shop.
Anchor to Step 1: Install HydrogenStep 1: Install Hydrogen
Create a project with your framework's CLI, like npx create-next-app@latest or npx create-react-router@latest, or use a project you already have. Then run the Hydrogen setup command from the project's root directory:
Terminal
The setup command does the following tasks:
- Installs
@shopify/hydrogenwith your project's package manager, which it detects from your lockfile. If@shopify/hydrogenis already in yourpackage.jsonfile but isn't installed, then it runs your package manager's install command instead. - Adds agent instructions that match the installed Hydrogen version to your project's
.claudeand.agentsdirectories.
The setup command doesn't change your app's code. You wire Hydrogen into your app in Step 3 and Step 4. You can follow those steps yourself, or open the project in your coding agent and ask it to follow them for you:
Prompt
The agent works through the same steps in the same order, and checks each one before it starts the next.
Anchor to Step 2: Configure environment variablesStep 2: Configure environment variables
Hydrogen reads its store connection from environment variables. Until you add a private Storefront API token, you can build against mock.shop, a public Storefront API with demo data that doesn't need a token.
Use the variable names in this step throughout your app. In Next.js, replace PUBLIC_ with NEXT_PUBLIC_, and use NEXT_PUBLIC_SHOP_ID.
Anchor to Use mock.shopUse mock. shop
mock.shop is a catalog of demo stores, each on its own host. The store at mock.shop sells apparel. For other catalogs, refer to the directory at mock.shop/llms.txt. To build against the demo catalog that's closest to your store, set your store domain to its host and leave the tokens out:
mock.shop connection
.envKeep the public Storefront API token commented out until you have one. An empty assignment, like PUBLIC_STOREFRONT_API_TOKEN=, loads as an empty string, and the Storefront API client rejects empty tokens. A missing token means tokenless access.
Connect a real store to test real checkout and customer accounts.
Anchor to Use your own storeUse your own store
Get your store domain and tokens from the Headless or Hydrogen channel in the Shopify admin, then add them to your project's .env file.
The private Storefront API token is server-only. Never expose it to the browser, and never give it a browser prefix like NEXT_PUBLIC_.
Store connection
.envIf you deploy to Oxygen and your storefront is linked to the Hydrogen channel, then you can pull these values with Shopify CLI instead of copying them by hand:
Terminal
The following table describes each variable:
| Variable | Required | Description |
|---|---|---|
PUBLIC_STORE_DOMAIN | Yes | Your store's myshopify.com domain, or a mock.shop host. |
PRIVATE_STOREFRONT_API_TOKEN | For a private client | The private Storefront API token. Server-only. |
PUBLIC_STOREFRONT_API_TOKEN | No | The public Storefront API token, for browser-safe clients. Leave it commented out until you have a token. |
SHOP_ID | For customer accounts | The numeric shop ID for customer accounts and Shopify's runtime scripts. |
PUBLIC_STOREFRONT_ID | For analytics | The Hydrogen storefront ID. Use 0 if your storefront doesn't have one. |
PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_ID | For customer accounts | The Customer Account API client ID. |
SESSION_SECRET | For customer accounts | A random secret, 32 characters or longer, that signs the session cookie. |
PUBLIC_CHECKOUT_DOMAIN | No | Your checkout domain, for app-level configuration like a Content Security Policy. Checkout links come from cart data, like cart.checkoutUrl, not from this variable. |
To generate a session secret, run the following command:
Terminal
Anchor to Step 3: Wire Hydrogen into your appStep 3: Wire Hydrogen into your app
This step connects Hydrogen to your app's request lifecycle. Complete each part in order, and check it before you start the next. After each part that changes code, run your app's typecheck script and fix any errors. Type errors compound, so they're easier to fix one part at a time.
Anchor to Keep environment access on the serverKeep environment access on the server
Read environment variables only in server code. Never read process.env, import.meta.env, or your framework's environment modules from client components or from modules that client components import.
Resolve values that come from environment variables on the server, and pass them to client components as props or loader data. For example, build the shop object that ShopifyScripts needs on the server. Browser code calls Shopify through Hydrogen's same-origin routes, so private tokens never leave the server.
For which files run on the server in React Router and Next.js, refer to Framework wiring.
Before you continue, confirm that no client component, and no module that a client component imports, reads environment variables.
Anchor to Set up route templatesSet up route templates
Hydrogen builds links to products, collections, and other Shopify resources from a shared route manifest. If your app already has routes for these resources, then keep them. Otherwise, use the standard paths:
| Page | Path |
|---|---|
| Collection listing | /collections |
| Collection | /collections/{handle} |
| Search results | /search |
| Product | /products/{handle} |
| Cart | /cart |
Use the plural /products, not /product. Some paths, like /api/cart, /checkout, and /cart/{variantId}:{quantity}, belong to Hydrogen's request handlers, so don't create pages for them.
To create the manifest, follow Configure route templates.
Before you continue, confirm that your route templates module exports your manifest. You check that Shopify's standard URLs reach your pages after you wire request handlers.
Anchor to Create the Storefront API clientCreate the Storefront API client
Create the Storefront API client for your framework by following Query the Storefront API. Then add hydrogen gql check to your existing typecheck script, so that the script validates your queries against the Storefront API schema. Run it after TypeScript and any route type generation:
Add GraphQL validation to typecheck
package.json
React Router
{
"scripts": {
"typecheck": "react-router typegen && tsc && hydrogen gql check --fail-on-warn"
}
}Next.js
{
"scripts": {
"typecheck": "next typegen && tsc --noEmit && hydrogen gql check --fail-on-warn"
}
}If you use mock.shop, then create a public client without a token, because mock.shop rejects private tokens. Refer to Use mock.shop during development.
For more information about query validation, refer to Type and validate Storefront API queries.
Before you continue, confirm that typecheck passes. You run your first query in Step 4.
Anchor to Wire request handlersWire request handlers
Run Hydrogen's request handlers before your router, and check for Shopify redirects after your router returns a 404. Follow Wire Hydrogen request handlers.
Then make the Storefront API client available to your pages:
React Router
Store the Storefront API client in router context from your root route's middleware, so that your loaders can read it:
Share the client with your loaders
app/lib/context.ts
Router context
import type {StorefrontClient} from '@shopify/hydrogen';
import {createContext} from 'react-router';
export const storefrontContext = createContext<StorefrontClient>();Root route middleware
import type {Route} from './+types/root';
import {storefrontContext} from './lib/context';
import {handleRequest} from './lib/shopify';
export const middleware: Route.MiddlewareFunction[] = [
({request, context}, next) =>
handleRequest(request, async (_request, storefrontClient) => {
context.set(storefrontContext, storefrontClient);
return next();
}),
];Next.js
Run the request handlers from proxy.ts. Server Components don't receive the client that proxy.ts creates. Catalog pages import the module-scoped client from Create a client for catalog reads. Personalized reads, like the cart, create a request-scoped client from headers() and wrap it in React's cache().
Store the Storefront API client in router context from your root route's middleware, so that your loaders can read it:
Share the client with your loaders
app/lib/context.ts
Router context
import type {StorefrontClient} from '@shopify/hydrogen';
import {createContext} from 'react-router';
export const storefrontContext = createContext<StorefrontClient>();Root route middleware
import type {Route} from './+types/root';
import {storefrontContext} from './lib/context';
import {handleRequest} from './lib/shopify';
export const middleware: Route.MiddlewareFunction[] = [
({request, context}, next) =>
handleRequest(request, async (_request, storefrontClient) => {
context.set(storefrontContext, storefrontClient);
return next();
}),
];For the full wiring in each framework, refer to Framework wiring.
Before you continue, confirm that typecheck passes. You check the request handlers in Step 4.
Anchor to Set up customer accountsSet up customer accounts
Customer accounts need a real store. If you're using mock.shop, then skip this part until you connect your store.
Create the customer session, and register the account handlers with your request handlers. Follow Steps 1 to 3 of Set up customer accounts.
Before you continue, confirm that typecheck passes.
Anchor to Step 4: Show products on a pageStep 4: Show products on a page
Add a page that queries the first few products in your store and lists them. Each product shows its title, its price, and a Buy now link. The page is the first place your app runs a Storefront API query, so it shows that your client, your request handlers, and your store connection work together.
React Router
List products
app/routes/home.tsxThe loader reads the client from the router context that your middleware sets in Wire request handlers.
Next.js
List products
app/page.tsxThe page reads the module-scoped catalog client from Create a client for catalog reads, because the product list is the same for every customer. If you use mock.shop, then create that client as a public client without a token.
Enable cacheComponents: true in next.config.ts. Cache Components require uncached data inside <Suspense>. The 'use cache' directive caches this query for an hour, so that Next.js can prerender the page. For more information, refer to Caching.
List products
app/routes/home.tsxThe loader reads the client from the router context that your middleware sets in Wire request handlers.
Keep the following in mind:
formatMoney()formats the amount and currency that Shopify returns. Pass your customer's locale instead ofen-USwhen your storefront supports more than one.- The Buy now link uses Shopify's cart permalink format,
/cart/{variantId}:{quantity}, with the number at the end of the variant ID. Hydrogen's request handlers answer it and redirect the customer to your store's checkout with that product in the cart, so you don't need a page for it.
Anchor to Check the pageCheck the page
Start your framework's development server, like npm run dev, and open the local URL that it prints. Then confirm the following:
- Your home page lists products from your store or from mock.shop, each with a price.
typecheckpasses, and your editor shows the query result's fields as typed./graphiqlreturns a200response.- If you connected your own store, then Buy now opens your store's checkout, and
/adminredirects to the Shopify admin. Buy now and/admindon't work with mock.shop. - If you set up HTTPS and customer accounts, then
/account/loginredirects to Shopify's OAuth/oauth/authorizeendpoint.
Customer accounts need HTTPS. To test sign-in locally, refer to Test sign-in locally with HTTPS.
Anchor to Step 5: Check your storefrontStep 5: Check your storefront
Run your app's own checks. Look in your package.json file for the following scripts, and run the ones that exist, in this order:
format, to fix formatting.lint,typecheck, orcheck. Confirm thattypecheckstill runshydrogen gql checkbefore you trust a passing result.build.test. If your project uses Playwright, then run it headless.format:check, if it's separate fromformat.
Run the checks again after every code change. A check that passed before your last change doesn't count.
Then test your storefront while it runs. Work through the production checklist against your development server, and against a production build if your framework can run one locally.
Anchor to Next stepsNext steps
Build the rest of your storefront's features in the following order. Later features build on earlier ones. For example, the cart drawer reuses your cart page's line items, and analytics events depend on Shopify's runtime scripts.
| Order | Feature | Guide |
|---|---|---|
| 1 | A home page that lists collections and products, built from the page in Step 4 | Query the Storefront API |
| 2 | Collection and search pages, with filters that sync with the URL | Build collection and search pages |
| 3 | Product pages, with a variant form and add to cart | Build product pages with variant selection |
| 4 | A /cart page that works without JavaScript, so you can check that items you add from a product page appear in the cart | Build cart UI |
| 5 | Shopify's runtime scripts, and your framework's navigation hook | Add analytics and consent |
| 6 | A cart drawer in your root layout, and a navbar with a cart trigger | Build a cart drawer |
| 7 | An account page with the customer's profile and a sign-out button, and an account link in the navbar | Set up customer accounts |
| 8 | Analytics events, sent only when the customer's consent allows them | Add analytics and consent |
Build your storefront's UI with the styling approach that your app already uses, like Tailwind CSS or CSS Modules.
Your app also owns image optimization and SEO. Use your framework's image component, or <img srcset> with Shopify CDN URLs that set sizing parameters from ImageTransformInput. Render meta tags, Open Graph tags, and structured data with your framework's metadata API and Storefront API data.
After you add each feature, run the checks in Step 5 again. When your storefront is ready, deploy it: