Implementing Global Content and Layout: A Technical Guide for Developers

This guide explains how to implement global content elements like headers, footers, and site-wide settings in a Pack-powered Hydrogen storefront from a technical perspective. You'll learn how to define global settings schemas, fetch global content in loaders, and create layout components to render consistent elements across your site.

Introduction to Global Content

Global content refers to elements that appear consistently across multiple pages of your storefront, such as:

  • Headers and navigation
  • Footers
  • Announcements and banners
  • Site-wide settings (colors, typography, etc.)
  • Social media links

Pack provides a structured approach to managing global content through Storefront Settings, which are accessible to content editors via the Customizer but defined by developers in code.

Global Content Implementation Process

Implementing global content in a Pack-powered Hydrogen storefront involves these steps:

  1. Register your Storefront Settings schema
  2. Fetch site settings in your root loader
  3. Pass site settings to PreviewProvider
  4. Read settings in your components

Let's walk through each step with code examples. They follow the Blueprint, which already has all four in place.

Step 1: Register Your Storefront Settings Schema

In Pack, global elements like headers, footers, and promotional banners are managed through Storefront Settings in the Customizer. As a developer, you define each group of settings in code and register them with registerStorefrontSettingsSchema from @pack/react. The Blueprint does this in app/settings/index.ts, with each group in its own file:

// app/settings/index.ts
import { registerStorefrontSettingsSchema } from '@pack/react';
import type { SiteSetting } from '@pack/types';

// Must be an array
const settings = [
  {
    label: 'Header',
    name: 'header',
    component: 'group',
    fields: [
      {
        label: 'Logo',
        name: 'logo',
        component: 'image',
      },
      {
        label: 'Menu Items',
        name: 'menuItems',
        component: 'group-list',
        itemProps: {
          label: '{{item.label}}',
        },
        fields: [
          {
            label: 'Label',
            name: 'label',
            component: 'text',
          },
          {
            label: 'Link',
            name: 'link',
            component: 'link',
          },
          {
            label: 'Submenu Items',
            name: 'submenuItems',
            component: 'group-list',
            itemProps: {
              label: '{{item.label}}',
            },
            fields: [
              {
                label: 'Label',
                name: 'label',
                component: 'text',
              },
              {
                label: 'Link',
                name: 'link',
                component: 'link',
              },
            ],
          },
        ],
      },
    ],
  },
  {
    label: 'Footer',
    name: 'footer',
    component: 'group',
    fields: [
      {
        label: 'Logo',
        name: 'logo',
        component: 'image',
      },
      {
        label: 'Footer Links',
        name: 'footerLinks',
        component: 'group-list',
        fields: [
          {
            label: 'Section Title',
            name: 'title',
            component: 'text',
          },
          {
            label: 'Links',
            name: 'links',
            component: 'group-list',
            itemProps: {
              label: '{{item.label}}',
            },
            fields: [
              {
                label: 'Label',
                name: 'label',
                component: 'text',
              },
              {
                label: 'Link',
                name: 'link',
                component: 'link',
              },
            ],
          },
        ],
      },
      {
        label: 'Copyright Text',
        name: 'copyright',
        component: 'text',
      },
    ],
  },
  {
    label: 'Theme Settings',
    name: 'theme',
    component: 'group',
    fields: [
      {
        label: 'Primary Color',
        name: 'primaryColor',
        component: 'color',
        defaultValue: '#000000',
      },
      {
        label: 'Secondary Color',
        name: 'secondaryColor',
        component: 'color',
        defaultValue: '#ffffff',
      },
      {
        label: 'Font Primary',
        name: 'fontPrimary',
        component: 'select',
        options: [
          { label: 'Sans Serif', value: 'sans-serif' },
          { label: 'Serif', value: 'serif' },
        ],
        defaultValue: 'sans-serif',
      },
    ],
  },
  {
    label: 'Announcement Bar',
    name: 'announcement',
    component: 'group',
    fields: [
      {
        label: 'Enable',
        name: 'enabled',
        component: 'toggle',
        defaultValue: false,
      },
      {
        label: 'Text',
        name: 'text',
        component: 'text',
      },
      {
        label: 'Link',
        name: 'link',
        component: 'link',
      },
      {
        label: 'Background Color',
        name: 'backgroundColor',
        component: 'color',
        defaultValue: '#000000',
      },
      {
        label: 'Text Color',
        name: 'textColor',
        component: 'color',
        defaultValue: '#ffffff',
      },
    ],
  },
];;

export function registerStorefrontSettings() {
  registerStorefrontSettingsSchema(settings as SiteSetting[]);
}

Call it once when your app loads, outside any component:

// app/root.tsx
import { registerStorefrontSettings } from '~/settings';

registerStorefrontSettings();

registerStorefrontSettingsSchema checks each group against Pack's settings format and logs a warning for any problem. Exporting the settings array alone isn't enough: Pack only sends registered settings to the Customizer.

Step 2: Fetch Site Settings in Your Root Loader

Query the site settings with the Pack client in your root loader, and return them with the rest of your root data:

// app/root.tsx
const SITE_SETTINGS_QUERY = `#graphql
  query SiteSettings($version: Version, $language: String, $country: String) @inContext(language: $language, country: $country) {
    siteSettings(version: $version) {
      id
      status
      settings
    }
  }
`;

export async function loader({ context }: Route.LoaderArgs) {
  const { pack, storefront } = context;

  const siteSettings = await pack.query(SITE_SETTINGS_QUERY, {
    variables: {
      country: storefront.i18n.country,
      language: storefront.i18n.language,
    },
    cache: storefront.CacheLong(),
  });

  return { siteSettings /* ...and your other root data */ };
}

Step 3: Pass Site Settings to PreviewProvider

Pass the loader's siteSettings to PreviewProvider from @pack/react, which wraps your layout. The Blueprint does this in app/components/Document/Document.tsx:

<PreviewProvider
  customizerMeta={customizerMeta}
  isPreviewModeEnabled={isPreviewModeEnabled}
  siteSettings={siteSettings}
>
  <Layout>{children}</Layout>
</PreviewProvider>

PreviewProvider holds the settings for your components. When an editor changes a setting in the Customizer, it updates them right away, so the change shows up in the preview before it's saved.

Step 4: Read Settings in Your Components

Read settings with the useSiteSettings hook from @pack/react. Each group is available under settings by its name:

import { useSiteSettings } from '@pack/react';

export function AnnouncementBar() {
  const siteSettings = useSiteSettings();
  const announcement = siteSettings?.settings?.announcement;

  if (!announcement?.enabled) return null;

  return (
    <div
      style={{
        backgroundColor: announcement.backgroundColor,
        color: announcement.textColor,
      }}
    >
      {announcement.link?.url ? (
        <a href={announcement.link.url}>{announcement.text}</a>
      ) : (
        announcement.text
      )}
    </div>
  );
}

Read settings through useSiteSettings rather than straight from the loader data, so live Customizer changes reach your components. The Blueprint wraps the hook in its own useSettings(), for example const { footer } = useSettings().

Was this page helpful?