Documentation Index
Fetch the complete documentation index at: https://docs.glood.ai/llms.txt
Use this file to discover all available pages before exploring further.
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
- Navigate to a product page - Should see
product_viewed events
- Search for products - Should see
search_submitted events
- Add item to cart - Should see
product_added_to_cart events
- 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:
- Debug mode is enabled:
debug: true
- Browser console is open
- Events are being triggered (navigate to product pages)
Issue: CSP Violations
Check:
- All Glood domains are in
connectSrc
- CSP headers are being applied correctly
- No typos in domain names
Issue: No Network Requests
Check:
- Customer consent is granted (try allowing all cookies)
- Pixel tracking is enabled (default:
enabled: true)
- 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: