Cross-Sell and Upsell Functionality
OrderItems can include upsell
and crossSell
arrays containing related offers that can be displayed to customers during checkout.
Understanding Upsells vs Cross-Sells
Upsells (
upsell
): Higher-tier versions of the current offer (e.g., upgrading from Basic to Premium)Cross-Sells (
crossSell
): Complementary products that enhance the primary purchase (e.g., adding antivirus software when buying a VPN)
Cascade Removal for Cross-Sells
Cross-sell items can be configured with cross_sell_cascade_removal__limio: true
in the offer attributes. When enabled, removing the parent offer will automatically remove all associated cross-sell items.
Example:
import React from "react"
import { useBasket } from "@limio/sdk"
const BasketWithCrossSells = () => {
const { orderItems, removeFromBasket } = useBasket()
return (
<div>
{orderItems.map(item => (
<div key={item.id}>
<h3>{item.name}</h3>
{/* Display upsell opportunities */}
{item.upsell && item.upsell.length > 0 && (
<div className="upsell-section">
<h4>Upgrade Options:</h4>
{item.upsell.map(upsellOffer => (
<div key={upsellOffer.path}>
<span>{upsellOffer.data.attributes.display_name__limio}</span>
{/* Add functionality to swap to upsell */}
</div>
))}
</div>
)}
{/* Display cross-sell opportunities */}
{item.crossSell && item.crossSell.length > 0 && (
<div className="crosssell-section">
<h4>You Might Also Like:</h4>
{item.crossSell.map(crossSellOffer => (
<div key={crossSellOffer.path}>
<span>{crossSellOffer.data.attributes.display_name__limio}</span>
{/* Add functionality to add cross-sell to basket */}
</div>
))}
</div>
)}
<button onClick={() => removeFromBasket(item)}>
Remove
{item.crossSell?.some(cs =>
cs.data.attributes.cross_sell_cascade_removal__limio
) && " (will also remove linked items)"}
</button>
</div>
))}
</div>
)
}
Implementation Example: Complete Upsell/Cross-Sell Flow
import React, { useState } from "react"
import { useBasket } from "@limio/sdk"
const CheckoutWithRecommendations = () => {
const { orderItems, addToBasket, swapOffer, removeFromBasket } = useBasket()
const [expandedItem, setExpandedItem] = useState(null)
const handleUpsell = async (itemId, upsellOffer) => {
await swapOffer(itemId, upsellOffer)
}
const handleCrossSell = async (crossSellOffer) => {
await addToBasket(crossSellOffer, { quantity: 1, sync: true })
}
return (
<div className="checkout-cart">
<h2>Your Cart</h2>
{orderItems.map(item => (
<div key={item.id} className="cart-item">
<div className="item-header">
<h3>{item.name}</h3>
<span className="price">
{item.price.currency} {item.price.amount}
</span>
</div>
{/* Show upsell options */}
{item.upsell?.length > 0 && (
<div className="upsell-banner">
<h4>🚀 Upgrade Available!</h4>
{item.upsell.map(upsell => (
<div key={upsell.path} className="upsell-option">
<span>{upsell.data.attributes.display_name__limio}</span>
<button onClick={() => handleUpsell(item.id, upsell)}>
Upgrade Now
</button>
</div>
))}
</div>
)}
{/* Show cross-sell options */}
{item.crossSell?.length > 0 && (
<div className="crosssell-section">
<button
onClick={() => setExpandedItem(
expandedItem === item.id ? null : item.id
)}
>
{expandedItem === item.id ? "Hide" : "Show"} Related Products
</button>
{expandedItem === item.id && (
<div className="crosssell-grid">
{item.crossSell.map(crossSell => (
<div key={crossSell.path} className="crosssell-card">
<h5>
{crossSell.data.attributes.display_name__limio}
</h5>
<button onClick={() => handleCrossSell(crossSell)}>
Add to Cart
</button>
</div>
))}
</div>
)}
</div>
)}
<button onClick={() => removeFromBasket(item)}>
Remove from Cart
</button>
</div>
))}
</div>
)
}
export default CheckoutWithRecommendations
Last updated
Was this helpful?