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 .nvmrc pins 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:

  1. Cloning the Blueprint Theme: Open your terminal and run the following command:

    npx @pack/create-hydrogen@latest
    

    This 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 .env file with sample values for Shopify's mock.shop.
    • Starts a new Git repository with an initial commit.
  2. Installing Dependencies: The CLI doesn't install packages. Move into the new folder and install them:

    cd your-project-name
    npm install
    
  3. Running Your Project Locally: Once the setup is complete, you can start your development server with:

    npm run dev
    

    This command serves your project on http://localhost:8080, allowing you to view and test your site in a local development environment.

  4. 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.

  5. 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

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

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:

IntegrationEnvironment variable
Google Analytics 4PUBLIC_GA4_TAG_ID
Google Tag ManagerPUBLIC_GTM_CONTAINER_ID
Meta PixelPUBLIC_META_PIXEL_ID
TikTok PixelPUBLIC_TIKTOK_PIXEL_ID
KlaviyoPUBLIC_KLAVIYO_API_KEY
ElevarPUBLIC_ELEVAR_SIGNING_KEY
BlotoutPUBLIC_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.

Was this page helpful?