Set up Blueprint
Get started
Create a free Pack account and explore our Quick Start guide.
Continue with the following guide or check out the Pack Storefront Blueprint README.
Requirements
- Node.js version 22.12.0 or newer (the Blueprint's
.nvmrcpins Node 24)
Using our CLI tool
To streamline the setup of your Blueprint Theme, we provide a command-line interface (CLI) tool that simplifies the process. Here’s how to use it:
-
Cloning the Blueprint Theme: Open your terminal and run the following command:
npx @pack/create-hydrogen@latestThis command does the following:
- Asks for a project name (for example,
your-project-name) and clones the Blueprint Theme into a new folder with that name. - Creates a
.envfile with sample values for Shopify's mock.shop. - Starts a new Git repository with an initial commit.
- Asks for a project name (for example,
-
Installing Dependencies: The CLI doesn't install packages. Move into the new folder and install them:
cd your-project-name npm install -
Running Your Project Locally: Once the setup is complete, you can start your development server with:
npm run devThis command serves your project on
http://localhost:8080, allowing you to view and test your site in a local development environment. -
No Pack Account Required: If you don't have a Pack account yet, no worries! The CLI tool allows you to run the Blueprint Theme with default Shopify data. This means you can start exploring the capabilities of the Hydrogen framework and how it integrates with Shopify without any initial setup or configuration.
-
Next Steps: After exploring the Blueprint Theme project, you might want to customize it further by connecting it to your Shopify Hydrogen storefront. You can do this by setting up environment variables as described below.
Environment Variables
To run your application locally, you can use Shopify's mock.shop API to simulate a Shopify storefront. You can set the PUBLIC_STORE_DOMAIN environment variable to mock.shop to use the mock.shop API.
SESSION_SECRET="foobar"
PUBLIC_STORE_DOMAIN="mock.shop"
PUBLIC_STOREFRONT_API_TOKEN="foobar"
You can automatically pull in your Shopify environment variables directly from your Shopify Hydrogen storefront using the Hydrogen CLI. Run the command below and follow its prompts. It downloads every variable set on the storefront, including the ones Shopify creates for you.
npx shopify hydrogen env pull
Please double check the .env file to ensure all necessary variables are present. If you set PACK_SECRET_TOKEN as a secret environment variable in Shopify, you must manually add it to your .env file.
Alternatively, manually create a .env file and copy the values from your Shopify Hydrogen storefront. Locate the variables by navigating to the Hydrogen storefront > Storefront Settings > Environments & Variables. The necessary variables include:
SESSION_SECRET="XXX"
PUBLIC_STOREFRONT_API_TOKEN="XXX"
PRIVATE_STOREFRONT_API_TOKEN="XXX"
PUBLIC_STORE_DOMAIN="XXX"
PUBLIC_STOREFRONT_ID="XXX"
PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_ID="XXX"
SHOP_ID="XXX"
PUBLIC_CHECKOUT_DOMAIN="XXX"
PACK_PUBLIC_TOKEN="XXX"
PACK_SECRET_TOKEN="XXX"
PACK_STOREFRONT_ID="XXX"
Shopify creates most of these when you create the storefront. Add PUBLIC_CHECKOUT_DOMAIN (your checkout domain, without https://) and the Pack variables yourself. You can find the Pack values in the Pack Admin when you connect your Hydrogen storefront.
Two optional variables affect Pack features:
PRIVATE_ADMIN_API_TOKEN: enables previewing draft products in the Customizer.PUBLIC_PACK_CONTENT_ENVIRONMENT: the handle of the Pack content environment to load. Defaults to your store's primary content environment.
Analytics integrations have their own variables. See Data Layer.
Building for Production
This command emulates the deployment process Shopify Oxygen uses when deploying your site live.
npm run build
Building for Local Development
This command initiates a server locally on your machine at http://localhost:8080.
npm run dev
Pack Customizer Content
Please ensure <RenderSections> component is always included on your routes, as the Customizer connection logic is handled within this component.
Access Pack Customizer data using the pack object in the Hydrogen context. The Blueprint's getPage() helper (in app/lib/server-utils/pack.server.ts) calls context.pack.query() and fetches every section of the page, 25 at a time. This example is a trimmed version of the Blueprint's product route, app/routes/($locale).products.$handle.tsx:
import {useLoaderData} from 'react-router';
import {RenderSections} from '@pack/react';
import {getPage} from '~/lib/server-utils/pack.server';
import {getSelectedProductOptions} from '~/lib/server-utils/product.server';
import {PRODUCT_PAGE_QUERY} from '~/data/graphql/pack/product-page';
import {PRODUCT_QUERY} from '~/data/graphql/storefront/product';
import type {Route} from './+types/($locale).products.$handle';
export async function loader({params, context, request}: Route.LoaderArgs) {
const {handle} = params;
const {storefront} = context;
if (!handle) throw new Response(null, {status: 404});
// Pack content for this product page, with every section
const {productPage} = await getPage({
context,
handle,
pageKey: 'productPage',
query: PRODUCT_PAGE_QUERY,
});
// Shopify product data from the Storefront API
const selectedOptions = await getSelectedProductOptions({
handle,
context,
request,
});
const {product} = await storefront.query(PRODUCT_QUERY, {
variables: {
handle,
selectedOptions,
country: storefront.i18n.country,
language: storefront.i18n.language,
},
cache: storefront.CacheShort(),
});
if (!product) throw new Response(null, {status: 404});
return {product, productPage};
}
export default function ProductRoute() {
const {product, productPage} = useLoaderData<typeof loader>();
return (
<div>
<h1>{product.title}</h1>
{productPage && <RenderSections content={productPage} />}
</div>
);
}
The productPage object contains all the Pack Section Setting content from CMS Authors in the Customizer, defined per Section's Setting schema. The product object holds Shopify-specific data from the Storefront API.
Refer to Section Schema API.
Caching
Pack uses the same caching strategies as the Hydrogen framework. The Pack client comes from the @pack/hydrogen package. The Blueprint creates it in server.ts and passes in Hydrogen's cache (from caches.open('hydrogen')) and waitUntil, which the client uses to cache Pack responses:
const pack = createPackClient({
cache,
waitUntil,
token: env.PACK_SECRET_TOKEN,
storeId: env.PACK_STOREFRONT_ID,
session: packSession,
testSession: packTestSession,
contentEnvironment: env.PUBLIC_PACK_CONTENT_ENVIRONMENT,
defaultThemeData,
});
Pass a strategy with the cache option on each query. The Blueprint uses CacheLong() for Pack content. For example, it loads site settings in app/lib/server-utils/settings.server.ts like this:
const {pack, storefront} = context;
const result = await pack.query(SITE_SETTINGS_QUERY, {
variables: {
country: storefront.i18n.country,
language: storefront.i18n.language,
},
cache: storefront.CacheLong(),
});
- If you don't pass
cache, Pack caches responses for 1 hour and serves stale content for up to 23 hours while it revalidates. - Responses with errors aren't cached.
- In preview mode (in the Customizer), queries skip the cache and return the latest unpublished content.
Data Layer
The Blueprint sends analytics events through Hydrogen's analytics components. app/contexts/AnalyticsProvider.tsx wraps the app in <Analytics.Provider> with the shop, cart, and consent settings. Hydrogen then publishes page views on every navigation and cart events, such as product_added_to_cart, when the cart changes. Routes and components publish the rest:
- Product pages render
<Analytics.ProductView>. - Collection pages render
<Analytics.CollectionView>. - The cart renders
<Analytics.CartView>. - Search renders
<Analytics.SearchView>.
For example, the product route (app/routes/($locale).products.$handle.tsx) publishes a product view once the cart is ready:
{isCartReady && (
<Analytics.ProductView
data={{
products: [
{
id: product.id,
title: product.title,
price: initialSelectedVariant?.price.amount || '0',
vendor: product.vendor,
variantId: initialSelectedVariant?.id || '',
variantTitle: initialSelectedVariant?.title || '',
quantity: 1,
},
],
}}
customData={{product, selectedVariant: initialSelectedVariant}}
/>
)}
The Blueprint adds its own custom events, such as variant selection, product clicks, and customer sign-ups, in app/components/Analytics/constants.ts.
Hydrogen sends these events to Shopify Analytics. The Blueprint's app/components/Analytics folder also forwards them to third-party tools, and app/components/Document/Scripts.tsx loads Google Tag Manager. Each integration turns on when you set its environment variable:
| Integration | Environment variable |
|---|---|
| Google Analytics 4 | PUBLIC_GA4_TAG_ID |
| Google Tag Manager | PUBLIC_GTM_CONTAINER_ID |
| Meta Pixel | PUBLIC_META_PIXEL_ID |
| TikTok Pixel | PUBLIC_TIKTOK_PIXEL_ID |
| Klaviyo | PUBLIC_KLAVIYO_API_KEY |
| Elevar | PUBLIC_ELEVAR_SIGNING_KEY |
| Blotout | PUBLIC_BLOTOUT_EDGE_URL |
To log events in the browser console, set PUBLIC_PACK_ANALYTICS_DEBUG="true".
SEO and Meta Tags
You can find the meta settings such as the title and description in the app/lib/server-utils/seo.server.ts file. Each route builds its tags with a function from this file, and most of them call getMeta().
By default, the site title is appended to the end of the page title using a | separator. For example, if the page title is "About Us" and the site title is "My Store," the resulting title tag will be "About Us | My Store." To keep a resource's SEO title as is, pass affixSiteTitleToSeoTitle: false to getMeta(). The Blueprint's product and collection functions already do this.
The site title comes from the Pack Admin under Settings > Site settings > Search engine optimization. If this field is empty, it uses your Shopify store name.
For the homepage (/), if the page doesn't have an SEO title, getMeta() uses the site title on its own instead of "Homepage | My Store":
if (page?.handle === '/') {
pageTitle = pageTitle === 'Homepage' ? siteTitle : pageTitle;
title = siteTitle;
}
To use a different homepage title, set an SEO title on the homepage's SEO settings in the Pack Admin. The site title is appended to it like any other page.
The getMeta() function also sets the meta description and Open Graph image (media) for the page:
- Description: uses the first one that's set: the resource's SEO description, the page's SEO description, the resource's description, the page's description, the site-wide SEO description, then your Shopify store description. Descriptions longer than 155 characters are shortened and end with
.... - Image: uses the page's SEO image, then the resource's SEO image. If neither is set, the page has no Open Graph image.
Additionally, the getMeta() function sets the robots meta tag (noIndex and noFollow) from the page's SEO settings. If page.seo.noIndex or page.seo.noFollow is true, the page's robots tag includes noindex or nofollow. You can set these in the page's SEO settings in the Pack Admin.