Loading Recommendations
The SDK fetches the recommendation sections you configure in the Glood dashboard — complete with full product data (title, handle, prices, images, options, and variants) — so you can render them with your own React components.
There are two ways to load recommendations:
| Method | Where it runs | Best for |
|---|
useRecommendations() hook | Browser (client-side) | Most storefronts — simplest integration |
fetchRecommendationSections() | Server (Hydrogen route loaders) | SEO-critical pages that need recommendations in the initial HTML |
Client-Side: useRecommendations Hook
Use the hook inside any component rendered under <GloodProvider>. The page URL, locale, and Glood client id are filled in automatically from the browser.
import { useRecommendations } from '@glood/hydrogen';
function ProductRecommendations({ productId }: { productId: string }) {
const { sections, loading, error } = useRecommendations({
pageType: 'product_page',
productId, // numeric Shopify product id, e.g. '8977367564515'
});
if (loading || error) return null;
return (
<>
{sections.map((section) => (
<section key={section.section_serve_id}>
<h2>{section.title}</h2>
<div className="product-grid">
{section.products.map((product) => (
<a key={product.product_id} href={`/products/${product.handle}`}>
<img src={product.image?.url} alt={product.title} />
<span>{product.title}</span>
<span>{product.price}</span>
</a>
))}
</div>
</section>
))}
</>
);
}
Shopify product ids in Hydrogen are GIDs like gid://shopify/Product/8977367564515. Pass only the numeric part: product.id.split('/').pop().
Page Types
Every request declares which page the visitor is on. Glood returns the sections configured for that page type in the dashboard:
| Page Type | Use On |
|---|
home | Homepage |
product_page | Product detail pages (pass productId) |
collection | Collection pages (pass collection id) |
cart | Cart page (pass cartProductIds) |
blog | Blog and article pages |
order_confirm | Order confirmation / thank-you page |
checkout, ajax_cart, returns, 404, other | Special pages |
Hook Result
The hook returns everything needed to render and attribute recommendations:
const {
sections, // RecommendationSection[] — sections with products
data, // Full SectionsResponse (request_id, experience, ...)
loading, // true while fetching
error, // Error | null
refetch, // Re-run the request with the same params
trackRender, // Attribution helpers (see below)
trackView,
trackClick,
trackAddToCart,
} = useRecommendations({ pageType: 'product_page', productId });
Deferring the Request
Pass { skip: true } as the second argument to wait until required params are ready:
const { sections } = useRecommendations(
{ pageType: 'product_page', productId },
{ skip: !productId },
);
Server-Side: fetchRecommendationSections
For SSR, fetch recommendations inside a Hydrogen route loader. Pass pageUrl and locale explicitly — there is no browser context on the server:
import { fetchRecommendationSections } from '@glood/hydrogen';
import { useLoaderData } from 'react-router';
export async function loader({ request, params, context }) {
const recommendations = await fetchRecommendationSections(
{
apiKey: context.env.GLOOD_API_KEY,
myShopifyDomain: 'your-store.myshopify.com',
},
{
pageType: 'product_page',
pageUrl: request.url,
locale: 'en-US',
productId: params.productId,
},
).catch(() => null); // Never let recommendations break the page
return { recommendations };
}
export default function ProductPage() {
const { recommendations } = useLoaderData();
return (
<>
{recommendations?.sections.map((section) => (
<section key={section.section_serve_id}>
<h2>{section.title}</h2>
{/* render section.products */}
</section>
))}
</>
);
}
Always wrap the call in a .catch() (or try/catch) inside loaders. A recommendations outage should degrade gracefully, not 500 your page.
Attribution Tracking
To attribute recommendation performance (views, clicks, and add-to-carts tied back to revenue in Glood analytics), fire the attribution helpers at the right moments. Each takes a SectionTrack object that links the interaction to the exact section render via sectionServeId:
function ProductRecommendations({ productId }) {
const { sections, data, trackRender, trackClick, trackAddToCart } =
useRecommendations({ pageType: 'product_page', productId });
return sections.map((section) => {
const baseTrack = {
section: String(section.id),
sectionServeId: section.section_serve_id, // from the API response
requestId: data?.request_id,
page: 'product_page',
parent: { productId },
};
return (
<section
key={section.section_serve_id}
ref={() => trackRender(baseTrack)}
>
<h2>{section.title}</h2>
{section.products.map((product) => (
<a
key={product.product_id}
href={`/products/${product.handle}`}
onClick={() =>
trackClick({
...baseTrack,
products: [{ productId: String(product.product_id) }],
})
}
>
{product.title}
</a>
))}
</section>
);
});
}
| Helper | Event Sent | When to Call |
|---|
trackRender(track) | glood:section:render | Section is rendered in the DOM |
trackView(track) | glood:section:view | Section scrolls into the viewport |
trackClick(track) | glood:section:click | Visitor clicks a recommended product |
trackAddToCart(track) | glood:section:add_to_cart | Visitor adds a recommended product to cart |
Standard events (page_viewed, product_added_to_cart, etc.) are sent automatically by GloodProvider — you never emit those manually. Attribution helpers are the only events you call yourself, because only your UI knows when a recommendation is clicked.
Experience Persistence
The sections API assigns each visitor an A/B experience. The SDK automatically persists the assigned experience id to localStorage (key gai_s_e) after every fetch, and every pixel event carries it — so all analytics segment correctly by experience without any work on your side.
Error Handling
Failed requests throw (or surface via the hook’s error) a GloodApiError carrying the HTTP status and response body:
import { GloodApiError } from '@glood/hydrogen';
try {
await fetchRecommendationSections(options, params);
} catch (error) {
if (error instanceof GloodApiError) {
console.error(error.status, error.body); // e.g. 401, { error: 'Invalid authentication' }
}
}
Common failures:
| Status | Cause |
|---|
401 | Wrong apiKey, or the key doesn’t match myShopifyDomain |
400 | Invalid params (bad pageType, missing pageUrl/locale), or shop plan not set up |
| CSP error in console | https://storefront.glood.ai missing from your connect-src — see Content Security Policy |
Empty sections: [] with a 200 is not an error — it means no sections are enabled for that page type in the Glood dashboard (or targeting rules excluded them).
See Also