Upsell & Cross-sell Usage
- Implementation in file
\app\routes\products.$handle.tsx
Configuration
getRecommendations
Function
You can customize the behavior of the getRecommendations
function by passing next parameters:
widgetIds
: widgetIds is a list of widget ids that you created on your Fast Simon dashboard. You can pass multiple widget ids to get recommendations from multiple widgets.productId
: productId of the product that the results will be based on, this is an optional field for non-personalized based widgets.
For a full documentation about creation widget in Fast Simon, go to Help center - Upsell & Cross-sell widgets.
Return Object
The function returns a Promise of an object type RecommendationsResponse
.
FastSimonWidget
Component
You can customize the FastSimonWidget
component with various props to match your store's design and layout requirements:
renderProduct
: A function that receives a product object and returns a React element. This allows you to customize the product card for each product in the widget.title
: The title of the widget, for example, "Similar Products" or "You may also like".breakpoints
: An object of breakpoints to control the number of products displayed on different devices.carouselGap
: The gap between products in the carousel (default value: 16px).RightArrowIcon
: A JSX element to use as the right arrow icon in the carousel. The left arrow is rotated automatically.imageAspectRatio
: The aspect ratio of the product image (default value: "2/3").
<FastSimonWidget
title={'Similar Products'}
products={recommendationsProducts}
renderProduct={(product, pos) => <MyProductCard key={product.id} product={product} />}
breakpoints={{
mobile: 2,
tablet: 3,
desktop: 4
}}
carouselGap={16}
RightArrowIcon={<MyRightArrowIcon />}
imageAspectRatio={"2/3"}/>
Usage Example
Imports
import {CacheLong} from '@shopify/hydrogen';
import {FastSimonWidget , transformToShopifyStructure} from '@fast-simon/storefront-kit';
... // additional imports
Loader Function
export async function loader(args: LoaderFunctionArgs) {
// Start fetching non-critical data without blocking time to first byte
const deferredData = loadDeferredData(args);
// Await the critical data required to render initial state of the page
const criticalData = await loadCriticalData(args);
// Your widgetID from your widgets at Fast Simon dashboard
const widgetIds = ["17301826035377900", "17301826035377911"];
const recommendationsResponse = args.context.fastSimon.getRecommendations({
props: {
widgetIds: widgetIds,
productId: criticalData.product.id
}
});
return defer({...deferredData,
...criticalData,
recommendationsData: recommendationsResponse.then(data => {
return {
uuid: data.uuid,
widget_responses: data.widget_responses.map(widget => {
return {
...widget,
products: transformToShopifyStructure(widget.products)
}
})
}
})
});
}
... // more functions and logic
Display Product
export default function Product() {
const {product, variants, recommendationsData} =
useLoaderData<typeof loader>();
const selectedVariant = useOptimisticVariant(
product.selectedVariant,
variants,
);
const {title, descriptionHtml} = product;
return (
<div className="product">
<ProductImage image={selectedVariant?.image} />
<div className="product-main">
<h1>{title}</h1>
<ProductPrice
price={selectedVariant?.price}
compareAtPrice={selectedVariant?.compareAtPrice}
/>
<br />
<Suspense
fallback={
<ProductForm
product={product}
selectedVariant={selectedVariant}
variants={[]}
/>
}
>
<Await
errorElement="There was a problem loading product variants"
resolve={variants}
>
{(data) => (
<ProductForm
product={product}
selectedVariant={selectedVariant}
variants={data?.product?.variants.nodes || []}
/>
)}
</Await>
</Suspense>
<br />
<br />
<p>
<strong>Description</strong>
</p>
<br />
<div dangerouslySetInnerHTML={{__html: descriptionHtml}} />
<br />
</div>
<Suspense fallback={<div className={'spinner-fallback'}><MyLoadingSpinner/></div> }>
<Await resolve={recommendationsData}>
{(data) => data.widget_responses.map((widget, index) => (
<FastSimonWidget key={index}
title={widget.title}
products={widget.products}
breakpoints={{
mobile: 2,
tablet: 3,
desktop: 4,
}}
RightArrowIcon={<MyRightArrowIcon/>}
imageAspectRatio="3/4"
/>
))}
</Await>
</Suspense>
</div>
);
}