UNPKG

@rebuy/hydrogen-sdk

Version:

The React/Hydrogen wrapper for the Rebuy Core SDK.

433 lines (311 loc) 13.9 kB
# @rebuy/hydrogen-sdk React components and hooks for integrating Rebuy's personalization engine into Shopify Hydrogen storefronts. ## Table of Contents - [Installation](#installation) - [Quick Start](#quick-start) - [Implementation Patterns](#implementation-patterns) - [Data Fetching Approaches](#data-fetching-approaches) - [Server-Side Rendering & Cart Reactivity (Recommended)](#server-side-rendering--cart-reactivity-recommended) - [Client-Side Implementation](#client-side-implementation) - [Components](#components) - [Hooks](#hooks) - [Context Builder](#context-builder) - [Configuration](#configuration) - [TypeScript Support](#typescript-support) - [Troubleshooting](#troubleshooting) ## Installation ```bash 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`: ```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 See [Choosing Your Approach](#choosing-your-approach-server-vs-client) section below for guidance on when to use server-side vs client-side patterns. ## Implementation Patterns The SDK supports both server-side and client-side rendering. ### Server-Side Rendering (Recommended) 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. ```typescript // Example: Get recommended products based on cart contents const recommendations = await rebuy.sdk.products.getRecommended({ ...rebuy.cartContext, limit: 4 }); ``` ### Data Sources (Recommended for Production) 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. ```typescript // 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`. ```typescript 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. ## Server-Side Rendering & Cart Reactivity (Recommended) 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. **Enhanced in v2.0:** Now automatically includes URL, geolocation, and language context! ```tsx // 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: ```tsx // 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> ); } ``` ```tsx // 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. ```tsx // 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. ```tsx // 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. ### `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. ```tsx 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: ```tsx 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: ```bash # .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. ```tsx 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.