Skip to content

Headless Hydrogen SDK

Package: @rebuy/[email protected] npm: npmjs.com/package/@rebuy/hydrogen-sdk

React components and hooks for integrating Rebuy's personalization engine into Shopify Hydrogen storefronts.

Table of Contents

Installation

npm install @rebuy/core-sdk @rebuy/hydrogen-sdk

Peer Dependencies:

  • React 18+
  • @shopify/hydrogen/react 2024.4+

Quick Start

1. Setup the Provider

Wrap your Hydrogen app with RebuyProvider in your root.tsx:

// app/root.tsx
import { RebuyProvider } from '@rebuy/hydrogen-sdk';

export default function App() {
    return (
        <html lang="en">
            <body>
                <RebuyProvider apiKey="your-rebuy-api-key">
                    <Layout>
                        <Outlet />
                    </Layout>
                </RebuyProvider>
            </body>
        </html>
    );
}

2. Choose Your Implementation Approach

The SDK supports both server-side and client-side rendering patterns. See the Implementation Patterns section below for guidance.

Implementation Patterns

The SDK supports both server-side and client-side rendering.

Server-side rendering is recommended for most use cases, including product recommendations, top sellers, and cart-based widgets. It offers better SEO, faster initial page loads, and built-in cart reactivity through Remix loaders.

Use Server-Side Rendering for:

  • Most recommendation widgets
  • SEO-critical content
  • Fast initial page loads

Client-Side Rendering

Client-side rendering is necessary for features that rely on browser-specific data, such as localStorage. The primary use case is for "Recently Viewed" products.

Use Client-Side Rendering for:

  • Recently Viewed products
  • Features requiring real-time user interaction in the browser

Data Fetching Approaches

There are two main approaches for fetching recommendation data: Direct API calls and Data Sources.

Direct API

The Direct API provides a straightforward way to get recommendations with minimal setup. It's suitable for prototyping or for widgets where admin-level configuration is not required.

// Example: Get recommended products based on cart contents
const recommendations = await rebuy.sdk.products.getRecommended({
    ...rebuy.cartContext,
    limit: 4,
});

Data Sources are the recommended approach for production environments. They unlock the full power of the Rebuy platform, allowing you to use the Rebuy Admin to configure business logic, run A/B tests, and manage targeting rules without changing your storefront code.

For example, you can create a rule like: IF customer is VIP AND cart total > $100 THEN show premium products.

A critical feature for cart-based widgets is the "Filter Input Products" setting in the Data Source configuration. When enabled, it automatically prevents items already in the cart from appearing in the recommendations.

// Example: Fetch from a Data Source, limiting the result to 4 products.
const recommendations = await rebuy.sdk.products.fetchFromDataSource(
    243093, // Your Data Source ID from the Rebuy Admin
    rebuy.cartContext,
    { limit: 4 } // Optional: Pass parameters like limit here
);

Enhancing Context with RebuyContextBuilder

For more precise targeting, the RebuyContextBuilder utility can be used with either Direct API calls or Data Sources to create the context object. It allows you to enrich the request with additional data, such as customer information, URL parameters, and geolocation.

Options that are specific to a single request, like limit, should be passed as the third argument to fetchFromDataSource.

const enhancedContext = new RebuyContextBuilder(rebuy.sdk)
    .merge(rebuy.cartContext)
    .withUrl(request.url)
    .withCustomer(customer)
    .build();

// Use the enhanced context and pass a limit
const recommendations = await rebuy.sdk.products.fetchFromDataSource(243093, enhancedContext, { limit: 4 });

When to use which approach:

  • Prototyping or simple needs: Use the Direct API.
  • Production, cart-based widgets, or dynamic business logic: Use Data Sources.
  • Improved targeting: Add the RebuyContextBuilder to either approach.

The SDK is designed to work seamlessly with Hydrogen's server-centric architecture. Using getRebuyData in your loaders is the most powerful feature of this SDK.

1. Using getRebuyData in Loaders

The getRebuyData helper function provides the Rebuy SDK instance and automatically enriched cart context for your Remix loaders.

// app/routes/($locale).products.$handle.tsx
import { getRebuyData, RebuyContextBuilder } from '@rebuy/hydrogen-sdk';
import { defer, type LoaderFunctionArgs } from '@shopify/remix-oxygen';

export async function loader({ context, params, request }: LoaderFunctionArgs) {
    // Get Rebuy SDK with automatically enriched context
    const { rebuy } = await getRebuyData({ context, request });

    // Option 1: Use the auto-generated context directly
    const recommendations = await rebuy.sdk.products.fetchFromDataSource('pdp-recommendations', {
        ...rebuy.cartContext, // Includes cart, URL, country, language automatically
        shopify_product_ids: params.productId,
    });

    // Option 2: Enhance the context further with RebuyContextBuilder
    const enhancedContext = new RebuyContextBuilder(rebuy.sdk)
        .merge(rebuy.cartContext) // Start with auto-generated context
        .withProduct({ id: product.id }) // Add current product
        .withCustomer({ id: customer.id, tags: customer.tags }) // Add customer data
        .build();

    const personalizedRecs = await rebuy.sdk.products.fetchFromDataSource(
        'personalized-recommendations',
        enhancedContext
    );

    return defer({ recommendations, personalizedRecs });
}

Automatic Context Enrichment:

When you pass the request parameter to getRebuyData, it automatically extracts:

  • Cart Context: cart_total, cart_item_count, shopify_product_ids, etc.
  • URL Context: url_path and url_params from the current request
  • Geolocation: country_code from Hydrogen's i18n configuration
  • Language: language from Hydrogen's i18n configuration

2. How Cart Reactivity Works

  1. Cart Mutation: User adds/removes items via Hydrogen's <CartForm>.
  2. Automatic Reload: Remix automatically re-runs loaders after cart mutations.
  3. Fresh Context: getRebuyData provides updated cart context.
  4. New Recommendations: Rebuy API returns fresh, cart-aware recommendations.
  5. UI Updates: Your component re-renders with the new data.

This pattern provides optimal performance, SEO, and a reactive user experience without complex client-side state management.

3. Complete Server-Side Widget Example

Here's a minimal, complete example of a server-side widget implementation:

// app/components/ServerTopSellers.tsx
import type { Product } from '@rebuy/core-sdk';

interface ServerTopSellersProps {
    products: Product[];
    title?: string;
}

export function ServerTopSellers({ products, title = 'Top Sellers' }: ServerTopSellersProps) {
    if (!products || products.length === 0) return null;

    return (
        <div className="top-sellers">
            <h2>{title}</h2>
            <div className="product-grid">
                {products.map((product) => (
                    <div key={product.id} className="product-card">
                        <img src={product.image?.src} alt={product.title} />
                        <h3>{product.title}</h3>
                        <p>${product.variants?.[0]?.price}</p>
                    </div>
                ))}
            </div>
        </div>
    );
}
// app/routes/($locale)._index.tsx
import { getRebuyData } from '@rebuy/hydrogen-sdk';
import { defer, type LoaderFunctionArgs } from '@shopify/remix-oxygen';
import { useLoaderData } from 'react-router';
import { ServerTopSellers } from '~/components/ServerTopSellers';

export async function loader({ context, request }: LoaderFunctionArgs) {
    const { rebuy } = await getRebuyData({ context, request });

    // Fetch data on the server
    const topSellers = await rebuy.sdk.products.getTopSellers({
        limit: 5,
        filter_oos: 'yes',
    });

    return defer({ topSellers });
}

export default function Homepage() {
    const { topSellers } = useLoaderData<typeof loader>();

    // Render with server-fetched data - no loading states needed!
    return <ServerTopSellers products={topSellers} />;
}

Key Benefits of This Pattern:

  • ✅ Products are in the initial HTML (view source to verify)
  • ✅ No loading spinners or layout shifts
  • ✅ SEO-friendly - search engines see the content
  • ✅ Automatically updates when cart changes (cart reactivity)

Client-Side Implementation

For features like Recently Viewed, a client-side implementation is more suitable.

1. Track Product Views

Add the RebuyProductView component to your product pages. It's invisible and handles analytics.

// app/routes/($locale).products.$handle.tsx
import { RebuyProductView } from '@rebuy/hydrogen-sdk';

export default function Product() {
    const { product } = useLoaderData<typeof loader>();

    return (
        <div className="product">
            <RebuyProductView
                product={{
                    id: product.id,
                    handle: product.handle,
                }}
            />
            <h1>{product.title}</h1>
            {/* ... */}
        </div>
    );
}

2. Display Recently Viewed Products

Use the RecentlyViewed component to show the products.

// app/components/RecentlyViewedSection.tsx
import { RecentlyViewed } from '@rebuy/hydrogen-sdk';

export function RecentlyViewedSection() {
    return (
        <section className="recently-viewed">
            <h2>Recently Viewed</h2>
            <RecentlyViewed limit={4} />
        </section>
    );
}

The RecentlyViewed component fetches its own data on the client-side.

Components

RebuyProvider

The root provider that initializes the SDK.

Props:

  • apiKey (required): Your Rebuy API key.
  • apiHost (optional): API host URL.
  • debug (optional): Enable debug logging.
  • nonce (optional): Content Security Policy nonce for inline scripts. Required when using CSP headers to ensure SDK scripts comply with your security policy.

RebuyProductView

Tracks product view events. Renders nothing.

Props:

  • product.id (required): Shopify GraphQL product ID.
  • product.handle (required): Product URL handle.

RecentlyViewed

Fetches and displays recently viewed products.

Props:

  • limit (optional): Number of products to fetch (default: 5).
  • className (optional): CSS class for the container.
  • loadingComponent (optional): Custom component for loading state.
  • emptyComponent (optional): Custom component for empty state.
  • onError (optional): Callback for handling errors.

Hooks

useRebuy

Access the underlying Rebuy SDK instance for direct API calls.

import { useRebuy } from '@rebuy/hydrogen-sdk';

function MyComponent() {
    const rebuy = useRebuy();

    const trackCustomEvent = async () => {
        await rebuy.tracking.productView('product-id');
    };

    return <button onClick={trackCustomEvent}>Track Event</button>;
}

Context Builder

The SDK exports RebuyContextBuilder from the core SDK for creating rich context objects:

import { RebuyContextBuilder } from '@rebuy/hydrogen-sdk';

// In your loader
const context = new RebuyContextBuilder(rebuy.sdk)
    .withCart(cart)
    .withProduct({ id: 'gid://shopify/Product/123' })
    .withCustomer({ id: 456, tags: ['vip'] })
    .withUrl(request.url)
    .withLocation('US')
    .withLanguage('en')
    .build();

const products = await rebuy.sdk.products.fetchFromDataSource('my-data-source', context);

Available Methods:

  • merge(context) - Merge an existing context object
  • withCart(cart) - Add cart data
  • withProduct(product) - Add product ID (chainable)
  • withCustomer(customer) - Add customer data
  • withUrl(url) - Extract URL path and parameters
  • withLocation(countryCode) - Add country code (ISO 3166-1)
  • withLanguage(language) - Add language code (ISO 639-1)
  • build() - Return the final context

Configuration

Environment Variables

Set up your environment variables:

# .env
PUBLIC_REBUY_API_KEY=your-rebuy-api-key
REBUY_SDK_DEBUG=true # optional

The getRebuyData helper and RebuyProvider will automatically use these.

TypeScript Support

This package is built with TypeScript and provides comprehensive type definitions.

import type { Product } from '@rebuy/core-sdk';
import type { RebuyProviderProps } from '@rebuy/hydrogen-sdk';

// Get product types from the core SDK
const products: Product[] = await rebuy.sdk.products.getTopSellers();

Troubleshooting

Debug Mode

Enable debug logging to troubleshoot issues by setting the REBUY_SDK_DEBUG=true environment variable for server-side logs, or window.REBUY_SDK_DEBUG = true in the browser console for client-side logs.

TypeScript Context Type Issue

If you encounter type errors with the context parameter in getRebuyData, you may need to cast it:

const { rebuy } = await getRebuyData({
    context: context as any, // Temporary workaround for type differences
    request,
});

This is a known issue that will be addressed in a future version.

See something that needs updating? Suggest an edit