Skip to main content

Basic Setup Example

This example shows how to set up the Glood Hydrogen SDK with minimal configuration for a typical e-commerce store.

Complete Implementation

1. Environment Variables

# .env
GLOOD_API_KEY=your_glood_api_key_here
PUBLIC_STORE_DOMAIN=your-store.myshopify.com
PUBLIC_CHECKOUT_DOMAIN=your-store.myshopify.com
PUBLIC_STOREFRONT_ID=your_storefront_id_here

2. Root Layout (app/root.tsx)

import {
  json,
  type LoaderFunctionArgs,
} from '@shopify/remix-oxygen';
import {
  Links,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
  useLoaderData,
} from '@remix-run/react';
import {
  Analytics,
  getShopAnalytics,
  useNonce,
  createContentSecurityPolicy,
} from '@shopify/hydrogen';
import { GloodProvider, createGlood } from '@glood/hydrogen';
import { recommendations, search, wishlist } from '@glood/hydrogen';

// Create Glood client with basic configuration
const glood = createGlood({
  apiKey: process.env.GLOOD_API_KEY!,
  myShopifyDomain: process.env.PUBLIC_STORE_DOMAIN!,
  debug: process.env.NODE_ENV === 'development',
})
  .use(recommendations())
  .use(search())
  .use(wishlist());

export async function loader({context}: LoaderFunctionArgs) {
  const {storefront, env} = context;

  // Configure CSP with Glood domains
  const {nonce, header} = createContentSecurityPolicy({
    shop: {
      checkoutDomain: env.PUBLIC_CHECKOUT_DOMAIN,
      storeDomain: env.PUBLIC_STORE_DOMAIN,
    },
    connectSrc: [
      'https://cdn.shopify.com',
      'https://*.shopifycloud.com',
      'https://events.glood.ai',
      'https://s-pixel.glood.ai',
      'https://w-pixel.glood.ai'
    ],
  });

  const analytics = getShopAnalytics({
    storefront,
    publicStorefrontId: env.PUBLIC_STOREFRONT_ID,
  });

  return json(
    {
      analytics,
      nonce,
    },
    {
      headers: {
        'Content-Security-Policy': header,
      },
    }
  );
}

export default function App() {
  const nonce = useNonce();
  const data = useLoaderData<typeof loader>();

  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width,initial-scale=1" />
        <Meta />
        <Links />
      </head>
      <body>
        <Analytics analytics={data.analytics}>
          <GloodProvider client={glood} loaderData={data}>
            <Layout>
              <Outlet />
            </Layout>
          </GloodProvider>
        </Analytics>
        <ScrollRestoration nonce={nonce} />
        <Scripts nonce={nonce} />
      </body>
    </html>
  );
}

3. Layout Component (app/components/Layout.tsx)

import {Await} from '@remix-run/react';
import {Suspense} from 'react';
import type {CartApiQueryFragment, HeaderQuery} from 'storefrontapi.generated';
import {Aside} from '~/components/Aside';
import {Footer} from '~/components/Footer';
import {Header, HeaderMenu} from '~/components/Header';
import {CartMain} from '~/components/Cart';

export type LayoutProps = {
  cart: Promise<CartApiQueryFragment | null>;
  children?: React.ReactNode;
  footer: Promise<FooterQuery>;
  header: HeaderQuery;
  isLoggedIn: boolean;
};

export function Layout({
  cart,
  children = null,
  footer,
  header,
  isLoggedIn,
}: LayoutProps) {
  return (
    <>
      <CartAside cart={cart} />
      <SearchAside />
      <MobileMenuAside menu={header.menu} shop={header.shop} />
      <Header header={header} cart={cart} isLoggedIn={isLoggedIn} />
      <main>{children}</main>
      <Suspense>
        <Await resolve={footer}>
          {(footer) => <Footer menu={footer.menu} shop={header.shop} />}
        </Await>
      </Suspense>
    </>
  );
}

function CartAside({cart}: {cart: LayoutProps['cart']}) {
  return (
    <Aside id="cart-aside" heading="CART">
      <Suspense fallback={<p>Loading cart ...</p>}>
        <Await resolve={cart}>
          {(cart) => {
            return <CartMain cart={cart} layout="aside" />;
          }}
        </Await>
      </Suspense>
    </Aside>
  );
}

function SearchAside() {
  return (
    <Aside id="search-aside" heading="SEARCH">
      <div className="predictive-search">
        <br />
        <p>Search functionality powered by Glood</p>
        <br />
      </div>
    </Aside>
  );
}

function MobileMenuAside({
  menu,
  shop,
}: {
  menu: HeaderQuery['menu'];
  shop: HeaderQuery['shop'];
}) {
  return (
    <Aside id="mobile-menu-aside" heading="MENU">
      <HeaderMenu
        menu={menu}
        viewport="mobile"
        primaryDomainUrl={shop.primaryDomain.url}
      />
    </Aside>
  );
}

What This Setup Provides

✅ Automatic Event Tracking

The basic setup automatically tracks these events:
  • Page Views - All page navigation
  • Product Views - Product detail page visits
  • Collection Views - Category/collection browsing
  • Cart Views - Shopping cart interactions
  • Search Queries - Product search submissions
  • Add to Cart - Product additions to cart
  • Remove from Cart - Product removals from cart

✅ Privacy Compliance

Default consent requirements:
  • Recommendations: Analytics + Marketing consent
  • Search: Analytics consent only
  • Wishlist: Analytics + Preferences consent

✅ Default Endpoints

  • Recommendations: https://storefront.glood.ai + https://events.glood.ai
  • Search: https://s-s.glood.ai + https://s-pixel.glood.ai
  • Wishlist: https://w-s.glood.ai + https://w-pixel.glood.ai

✅ Debug Logging

In development mode, you’ll see console logs like:
[Glood Debug] Setting up event subscriptions for apps: ['recommendations', 'search', 'wishlist']
[Glood Debug] Received product_viewed event, distributing to interested apps
[Glood Debug] Processing product_viewed event for recommendations
[Glood Debug] Consent granted for recommendations, processing event
[Glood Debug] Successfully sent event to https://events.glood.ai

Testing the Setup

1. Verify Installation

Check that the SDK is working by looking for debug logs in the browser console.

2. Test Event Tracking

  1. Navigate to a product page - Should see product_viewed events
  2. Search for products - Should see search_submitted events
  3. Add item to cart - Should see product_added_to_cart events
  4. View cart - Should see cart_viewed events

3. Verify Network Requests

In browser DevTools Network tab, look for successful POST requests to:
  • https://events.glood.ai
  • https://s-pixel.glood.ai
  • https://w-pixel.glood.ai

Customizing the Basic Setup

Add Custom Settings

const glood = createGlood({
  apiKey: process.env.GLOOD_API_KEY!,
  myShopifyDomain: process.env.PUBLIC_STORE_DOMAIN!,
  debug: process.env.NODE_ENV === 'development',
  settings: {
    storeVersion: '2.0',
    customTrackingId: 'custom-123',
  },
});

Selective App Usage

// Only enable recommendations and search
const glood = createGlood({
  apiKey: process.env.GLOOD_API_KEY!,
  myShopifyDomain: process.env.PUBLIC_STORE_DOMAIN!,
})
  .use(recommendations())
  .use(search());
  // .use(wishlist()); // Commented out

Environment-Specific Debug

const glood = createGlood({
  apiKey: process.env.GLOOD_API_KEY!,
  myShopifyDomain: process.env.PUBLIC_STORE_DOMAIN!,
  debug: process.env.NODE_ENV !== 'production', // Debug in dev and staging
});

Using the Glood Client

Access Client in Components

import { useGloodAnalytics } from '@glood/hydrogen';

function MyComponent() {
  const glood = useGloodAnalytics();

  if (!glood) {
    return <div>Glood not available</div>;
  }

  const enabledApps = glood.getEnabledApps();
  const isDebugMode = glood.debug;

  return (
    <div>
      <p>Debug mode: {isDebugMode ? 'On' : 'Off'}</p>
      <p>Enabled apps: {enabledApps.map(app => app.name).join(', ')}</p>
    </div>
  );
}

Custom Analytics Integration

import { useGloodAnalytics } from '@glood/hydrogen';
import { useEffect } from 'react';

function CustomAnalytics() {
  const glood = useGloodAnalytics();

  useEffect(() => {
    if (!glood) return;

    const recommendationsApp = glood.getApp('recommendations');
    if (recommendationsApp) {
      console.log('Recommendations endpoint:', recommendationsApp.endpoint);
      console.log('Pixel enabled:', recommendationsApp.pixel.enabled);
    }
  }, [glood]);

  return null;
}

Common Issues and Solutions

Issue: No Debug Logs

Check:
  1. Debug mode is enabled: debug: true
  2. Browser console is open
  3. Events are being triggered (navigate to product pages)

Issue: CSP Violations

Check:
  1. All Glood domains are in connectSrc
  2. CSP headers are being applied correctly
  3. No typos in domain names

Issue: No Network Requests

Check:
  1. Customer consent is granted (try allowing all cookies)
  2. Pixel tracking is enabled (default: enabled: true)
  3. Apps are properly registered with .use()

Issue: TypeScript Errors

Solution:
npm install --save-dev @types/react @types/node

Next Steps

Support

If you need help with the basic setup: