Hydrogen + Pack Integration: Technical Implementation Guide
Integrating Pack into your existing Hydrogen project is simple and takes just a few minutes.
Pack simplifies building and managing your Hydrogen storefront with easy-to-use developer APIs, CRO tools, and a visual page editor. This makes it easier and faster to customize and enhance your storefront needs.
Prerequisites
- A Shopify Hydrogen project on React Router 7 (
@shopify/hydrogen^2026.1.0,react-router^7.12.0). Still on Remix? Follow the React Router 7 migration guide first. - A Pack account
- Node.js version 20 or higher
npm(or your package manager of choice, such asyarnorpnpm)
Install the Pack dependencies
- Install the latest version of
@pack/hydrogen
npm
npm install @pack/hydrogen@latest
- Install the latest version of
@pack/react
npm
npm install @pack/react@latest
Update env.d.ts (optional if using Typescript)
If you are using typescript in your Hydrogen project, you will need to add some additional Pack types to your environment definition types.
env.d.ts
interface Env {
SESSION_SECRET: string
PUBLIC_STOREFRONT_API_TOKEN: string
PRIVATE_STOREFRONT_API_TOKEN: string
PUBLIC_STORE_DOMAIN: string
PUBLIC_STOREFRONT_API_VERSION: string
PUBLIC_STOREFRONT_ID: string
PACK_PUBLIC_TOKEN: string
PACK_SECRET_TOKEN: string
PACK_STOREFRONT_ID: string
PACK_CONTENT_ENVIRONMENT?: string
PUBLIC_PACK_CONTENT_ENVIRONMENT?: string
PACK_API_URL?: string
PRIVATE_SHOPIFY_CHECKOUT_DOMAIN?: string
PRIVATE_SHOPIFY_STORE_MULTIPASS_SECRET?: string
}
env.d.ts
import type {Pack} from '@pack/hydrogen';
...
declare module 'react-router' {
export interface AppLoadContext {
session: HydrogenSession;
storefront: Storefront;
env: Env;
pack: Pack;
}
}
Modify your server.ts
- Import
createPackClient,PackSession,PackTestSession, andhandleRequestfrom@pack/hydrogen, and the React Router server build and Oxygen helpers.
server.ts
// Virtual entry point for the app
import * as serverBuild from 'virtual:react-router/server-build'
import {
createRequestHandler,
getStorefrontHeaders,
} from '@shopify/hydrogen/oxygen'
import {
createPackClient,
PackSession,
PackTestSession,
handleRequest,
} from '@pack/hydrogen'
- Initialize Pack's sessions where you initialize the
AppSession.PackSessiontracks whether you are in preview mode, andPackTestSessionstores A/B test assignments.
server.ts
const [cache, session, packSession, packTestSession] = await Promise.all([
caches.open('hydrogen'),
AppSession.init(request, [env.SESSION_SECRET]),
PackSession.init(request, [env.SESSION_SECRET]),
PackTestSession.init(request, [env.SESSION_SECRET]),
])
- Creating the
packclient
server.ts
const pack = createPackClient({
cache,
waitUntil,
storeId: env.PACK_STOREFRONT_ID,
token: env.PACK_SECRET_TOKEN,
session: packSession,
testSession: packTestSession,
contentEnvironment: env.PUBLIC_PACK_CONTENT_ENVIRONMENT,
})
- Adding the Pack client to app load context. By adding
packto your loader context, you can use this Pack client to make requests to fetch data from Pack's CMS API
server.ts
const response = await handleRequest(
pack,
request,
createRequestHandler({
build: serverBuild,
mode: process.env.NODE_ENV,
getLoadContext: () => ({
cache,
waitUntil,
session,
storefront,
cart,
env,
pack,
}),
}),
)
handleRequest also answers POST /pack/consent, which PackProvider uses to pass the visitor's answer to Shopify's privacy banner to Pack. If you use another consent tool, see Consent Management. To target signed-in customers with audiences, also pass getCustomerContext to createPackClient; see Targeting signed-in customers.
Adding preview route
By adding this api.edit.tsx route, this will allow the customizer and preview links to authenticate and set your storefront in preview mode to pull in draft content.
- Create an
api.edit.tsxfile in./app/routes - Add in the action and loader handlers
/app/routes/api.edit.tsx
import { previewModeAction, previewModeLoader } from '@pack/hydrogen'
import type { ActionFunction, LoaderFunction } from 'react-router'
export const action: ActionFunction = previewModeAction
export const loader: LoaderFunction = previewModeLoader
Add Pack's PackProvider to your root.tsx
- Request Pack data in root loader
/app/root.tsx
import type {Route} from './+types/root';
export async function loader({context}: Route.LoaderArgs) {
const {pack} = context;
const isPreviewModeEnabled = pack.isPreviewModeEnabled();
const siteSettings = await pack.query(SITE_SETTINGS_QUERY, {});
return {
...pack.getPackContextData(),
customizerMeta: pack.session.get('customizerMeta'),
isPreviewModeEnabled,
siteSettings,
...
};
}
const SITE_SETTINGS_QUERY = `#graphql
query SiteSettings($version: Version) {
siteSettings(version: $version) {
id
status
settings
publishedAt
createdAt
updatedAt
favicon
seo {
title
description
keywords
}
}
}
`
- Wrap your app in the
PackProvider(PreviewProvideris an alias). This will provide context to get settings and other data from Pack.
/app/root.tsx
import {PackProvider} from '@pack/react';
import {Outlet, useLoaderData} from 'react-router';
...
export default function App() {
const {customizerMeta, isPreviewModeEnabled, siteSettings} =
useLoaderData<typeof loader>();
return (
<html lang="en">
<head>...</head>
<body>
<PackProvider
customizerMeta={customizerMeta}
isPreviewModeEnabled={isPreviewModeEnabled}
siteSettings={siteSettings}
>
<Outlet />
</PackProvider>
...
</body>
</html>
);
}
You're done!
After this you are set to start querying data in your route loaders and rendering sections. You should be able to use the customizer and edit and add new sections.
Every Pack content query whose result is passed to <RenderSections> must
return handle and template { type }. The Customizer uses these fields to
identify the current route and load its editable content. A page can render
normally without template.type, but its Customizer sidebar will remain in a
loading state.
You can take a look at our Blueprint to see how we structure and implement a Hydrogen project to manage and render sections.
Feel free to also take a look at some of our additional resources and guides below to learn more about storefront development and customization.