
Cross Sell Upsell Engine
- 75 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Recommend complementary and premium products in cart, at checkout, and post-purchase using purchase patterns and margin optimization to raise average order value.
About
A skill for adding cross-sell and upsell recommendations across cart, checkout, and post-purchase placements via platform apps. A developer uses it to lift average order value without custom recommendation code.
- Choose placement (PDP, cart, post-purchase) and recommendation logic
- App-driven; start with one placement and measure
Cross Sell Upsell Engine by the numbers
- 75 all-time installs (skills.sh)
- Ranked #1,202 of 1,879 Marketing & SEO skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill cross-sell-upsell-engineAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 75 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Recommend complementary and premium products in cart, at checkout, and post-purchase using purchase patterns and margin optimization to raise average order value.
Files
Cross-Sell and Upsell Engine
Overview
Cross-sells and upsells generate 10–30% incremental revenue with minimal customer acquisition cost. Every major e-commerce platform has apps that handle the recommendation logic without custom code. The key decisions are: which placement to start with (PDP, cart, or post-purchase), what recommendation logic to use (manual bundles vs. algorithm-based), and how to avoid checkout friction. Start with one placement and measure before expanding.
When to Use This Skill
- When average order value (AOV) is below industry benchmarks and you want to grow it without paid traffic
- When launching a new recommendation widget on PDP, cart, or checkout pages
- When replacing a generic "You may also like" carousel with affinity-based personalization
- When building a bundle builder or "complete the look" feature
- When wanting to A/B test recommendation placements
Core Instructions
Step 1: Choose the right tool for your platform
| Platform | Recommended Tool | Why |
|---|---|---|
| Shopify | Rebuy (most powerful) or Frequently Bought Together | Rebuy uses AI-based recommendations with multiple placement types; FBT is simpler and cheaper for basic "people also bought" |
| Shopify (Plus) | Rebuy + Shopify Functions | Shopify Functions allows custom cart transforms for bundle discounts |
| WooCommerce | WooCommerce built-in cross-sell/upsell + YITH WooCommerce Frequently Bought Together | WooCommerce has native upsell and cross-sell fields on every product; YITH adds the "FBT" widget |
| BigCommerce | Also Bought or Boost Commerce (App Marketplace) | Both integrate natively with BigCommerce product catalog |
| Custom / Headless | Rebuy API or Recombee | Both offer recommendation APIs; Rebuy integrates directly with Shopify/BigCommerce backends |
Step 2: Decide on placement — start with one
| Placement | Expected AOV Lift | Conversion Risk | Start Here? |
|---|---|---|---|
| Product page (below Add to Cart) | Moderate | Low | Yes — best starting point |
| Cart page (sidebar or bottom) | High | Low | Yes — high intent, low friction |
| Post-purchase page | High | None (order already placed) | Yes — zero risk to conversion |
| Checkout page | High | Medium-High | No — test this last; can hurt CVR |
Recommendation: start with product page + cart page. Add post-purchase after measuring results. Only add checkout recommendations if you have data showing they lift revenue without hurting CVR.
Step 3: Set up recommendations on your platform
---
Shopify
Using Rebuy (recommended for full-featured setup):
1. Install Rebuy from the Shopify App Store 2. Go to Rebuy → Smart Cart to enable AI-powered cart recommendations — configure the number of products to show (2–3) and placement (cart drawer or cart page) 3. Go to Rebuy → Product Page Widgets to add a "Frequently Bought Together" widget below the Add to Cart button 4. Go to Rebuy → Post-Purchase Offers to add a one-click upsell on the order confirmation page 5. Rebuy uses Shopify's order history to compute co-purchase affinity automatically — no manual configuration needed 6. To create manual bundles: go to Rebuy → Data Sources → Manual Recommendations and pair specific products
Using Frequently Bought Together (simpler, cheaper):
1. Install from the Shopify App Store 2. The app automatically analyzes order history to suggest product pairs 3. Review auto-generated bundles under FBT → Bundles and remove irrelevant pairs 4. Configure the widget appearance to match your theme 5. Set a bundle discount (optional) — 5–10% off when both products are added together
For post-purchase upsells on Shopify:
- Use ReConvert or CartHook for post-purchase one-click upsell pages
- These show immediately after checkout is completed but before the thank-you page
---
WooCommerce
Using WooCommerce native cross-sells and upsells:
1. Go to WooCommerce → Products → [Edit any product] 2. In the Linked Products tab, add:
- Upsells: products to show on the product page as "You may also like" (higher-priced alternatives)
- Cross-sells: products to show in the cart sidebar
3. WooCommerce shows cross-sells in the cart automatically — no additional plugin needed
Using YITH WooCommerce Frequently Bought Together (free version available):
1. Install from the WordPress plugin directory 2. Go to YITH → Frequently Bought Together → General Settings and configure the widget title and discount amount 3. On each product's edit page, go to the FBT tab and manually select companion products, or enable auto-recommendations 4. The widget appears below the Add to Cart button automatically
For post-purchase upsells on WooCommerce:
- Use CartFlows + AutomateWoo for post-purchase funnel pages
- Or use WooFunnels / FunnelKit which includes post-purchase upsell flows
---
BigCommerce
1. Install Also Bought from the BigCommerce App Marketplace 2. The app analyzes your order history and automatically generates "Customers Also Bought" recommendations 3. Configure placement (product page, cart page) and number of products shown in the app settings 4. For manual control: go to the product editor in BigCommerce Admin and use the Related Products feature to manually specify related items
---
Custom / Headless
Use Rebuy's API or Recombee for headless storefronts:
Rebuy API (if your backend is Shopify/BigCommerce):
// Fetch recommendations from Rebuy for a given product
const response = await fetch(
`https://rebuyengine.com/api/v1/products/recommended?key=${REBUY_API_KEY}&shopify_product_ids=${productId}&limit=4`
);
const { data } = await response.json();Build co-purchase recommendations from order data (only if no third-party tool):
// Compute product affinity from co-purchase frequency
// Run nightly — minimum 1,000 orders for meaningful signal
async function computeProductAffinity() {
const orders = await db.orders.findAll({ where: { status: 'completed' }, include: ['lineItems'] });
const coMatrix: Record<string, Record<string, number>> = {};
for (const order of orders) {
const productIds = [...new Set(order.lineItems.map((li: any) => li.productId))];
for (let i = 0; i < productIds.length; i++) {
for (let j = i + 1; j < productIds.length; j++) {
const [a, b] = [productIds[i], productIds[j]].sort();
coMatrix[a] = coMatrix[a] ?? {};
coMatrix[a][b] = (coMatrix[a][b] ?? 0) + 1;
}
}
}
// Store results and filter to pairs with at least 3 co-purchases
// Serve from a cached API endpoint with 1-hour TTL
}Step 4: Configure pricing and discount strategy
- Bundle discounts: 5–10% off when both products are added together — enough to motivate, not enough to erode margin
- Upsell price range: show upsells priced 10–50% above the current product; upsells above 2× the original price rarely convert
- Checkout recommendations: limit to 1–2 low-cost add-ons (under $30) — multiple recommendations at checkout increase abandonment
- Post-purchase offers: these can be higher-priced since the customer is already in a buying mindset
Step 5: Measure results
Track these metrics weekly in your recommendation app's analytics:
| Metric | Target | Where to Find |
|---|---|---|
| Recommendation CTR | 8–15% (PDP), 15–25% (cart) | Rebuy → Analytics, FBT → Reports |
| Orders with recommended item added | 5–15% of all orders | App dashboard → Attach rate |
| AOV lift from recommendations | $5–$20 depending on catalog | Compare AOV of orders with/without recommendation clicks |
Best Practices
- Exclude out-of-stock items from recommendations — always — nothing is more frustrating than clicking a recommendation that is unavailable (Rebuy and most apps handle this automatically)
- Start with 3–4 recommendations max — showing more products creates decision paralysis and reduces CTR
- Manual overrides for new products — new SKUs have no purchase history; manually configure them as recommended alongside bestsellers for the first 30 days (all major apps have manual override)
- Refresh auto-recommendations after seasonal changes — purchasing patterns shift; review recommendations quarterly
- Track recommendation attribution separately — tag orders where a recommended item was added so you can measure true incremental AOV
Common Pitfalls
| Problem | Solution |
|---|---|
| Checkout recommendations increase cart abandonment | A/B test before enabling; limit to 1 low-cost item under $30; remove if test shows negative impact |
| Recommendations show the same product being viewed | Exclude the current product from recommendations (most apps do this automatically; verify in settings) |
| Cold start — no recommendations for new products | Add manual recommendations in your app's admin panel; pair new products with bestsellers |
| Recommendations irrelevant (e.g., suggest a phone case with a t-shirt) | Review auto-generated recommendations and block irrelevant pairs using the "block" feature in your app |
| Bundle discount codes being shared publicly | Use your app's built-in auto-apply discount (no code to share) rather than coupon codes |
Related Skills
- @predictive-personalization
- @customer-retention-engine
- @conversion-rate-optimization
- @loyalty-program-optimization
- @email-marketing-automation
{
"context": "Tests whether the agent computes product affinity using the correct lift formula with confidence, applies the right minimum support threshold, excludes bulk orders, and schedules the job correctly.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Lift formula",
"max_score": 12,
"description": "Affinity score is computed as lift = P(A∩B) / (P(A) * P(B)) — the code must divide the joint probability by the product of individual probabilities"
},
{
"name": "Confidence computed",
"max_score": 8,
"description": "Confidence P(B|A) = coCount / countA (or pAB / pA) is also computed and stored alongside lift"
},
{
"name": "Minimum support threshold",
"max_score": 12,
"description": "Pairs with fewer than 3 co-purchases are excluded from affinity records (coCount >= 3 or equivalent)"
},
{
"name": "Bulk order exclusion",
"max_score": 12,
"description": "Orders containing more than 15 line items are excluded from the co-purchase computation"
},
{
"name": "Bidirectional pairs stored",
"max_score": 8,
"description": "For each product pair (A, B), both (A→B) and (B→A) affinity records are written so either product can be used as the source lookup key"
},
{
"name": "Upsert on duplicate",
"max_score": 8,
"description": "Existing affinity records are upserted (updated) rather than duplicated — uses updateOnDuplicate or equivalent ON CONFLICT / upsert logic"
},
{
"name": "Nightly schedule",
"max_score": 10,
"description": "design-notes.md states the job runs nightly (e.g. via cron, scheduled job, or task scheduler) — not ad-hoc or on every request"
},
{
"name": "Only completed orders",
"max_score": 10,
"description": "The query filters for completed orders only (status = 'completed' or equivalent) — pending/cancelled orders are not included"
},
{
"name": "Design notes formula explanation",
"max_score": 10,
"description": "design-notes.md explains the lift formula (P(A∩B) / P(A)*P(B)) in plain language"
},
{
"name": "Minimum threshold rationale",
"max_score": 10,
"description": "design-notes.md explains WHY a minimum co-purchase count is required (noise/signal quality) — not just that it is applied"
}
]
}
Nightly Product Affinity Computation Job
Problem/Feature Description
A mid-size outdoor equipment retailer has accumulated three years of order data across 8,000+ SKUs. Their current recommendation system uses simple category matching ("other products in Camping > Tents"), which drives poor click-through rates because it surfaces duplicates and irrelevant items. The analytics team has identified that customers who buy tent poles almost always buy tent stakes and groundsheets — but the system never surfaces these connections.
The engineering team wants to replace the category-matching approach with a data-driven affinity computation job. This job will process completed orders from the database, calculate which product pairs appear together frequently beyond what random chance would predict, and write the resulting scores back to a product_affinities table for the recommendation API to query. The job must be designed to run unattended on a schedule and produce reliable signal — meaning it needs to handle noise from low-volume pairs and avoid distorted scores introduced by wholesale/bulk purchase orders.
Output Specification
Write a self-contained TypeScript module affinity-job.ts that implements the full affinity computation pipeline. The module should export the main computation function and include inline comments explaining the algorithm.
Also produce a short design-notes.md explaining:
- How the affinity score is calculated (the formula used)
- What filtering is applied to orders before processing
- Why a minimum co-purchase count is required before a pair is recorded
- How the job is intended to be scheduled
{
"context": "Tests whether the agent implements the three-layer recommendation strategy (manual overrides → affinity → trending fallback), correct placement configuration per page type, upsell price guardrails, exclusion of out-of-stock and source products, and correct score aggregation.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Manual overrides first",
"max_score": 8,
"description": "getRecommendations queries manual overrides first and returns them with highest priority — if manual recs fill the limit, affinity layer is skipped"
},
{
"name": "Affinity layer second",
"max_score": 8,
"description": "Affinity-based results (from product_affinities or equivalent) are used as the second layer when manual overrides don't fill the limit"
},
{
"name": "Trending fallback third",
"max_score": 8,
"description": "A trending/bestseller fallback is added as the third layer when affinity results don't fill the limit"
},
{
"name": "Default limit is 4",
"max_score": 6,
"description": "When no limit is provided in the request, getRecommendations defaults to returning 4 results"
},
{
"name": "Exclude out-of-stock",
"max_score": 8,
"description": "Recommendations filter out products that are out of stock (stockQuantity <= 0 or inactive)"
},
{
"name": "Exclude source products",
"max_score": 8,
"description": "Source productIds (and any explict excludeIds) are excluded from the results — a product is never recommended alongside itself"
},
{
"name": "Over-fetch 3x",
"max_score": 8,
"description": "Affinity query fetches limit * 3 candidates to allow for deduplication before slicing to the final limit"
},
{
"name": "Score aggregation",
"max_score": 8,
"description": "When multiple source products produce the same target, scores are aggregated (summed) rather than taking just the first or highest individual score"
},
{
"name": "Upsell price range",
"max_score": 8,
"description": "getUpsellCandidates filters products priced between 110% and 150% of the source product price"
},
{
"name": "Upsell ordering",
"max_score": 6,
"description": "Upsell candidates are ordered by reviewScore descending, then salesCount descending"
},
{
"name": "Checkout limit and maxPrice",
"max_score": 8,
"description": "PLACEMENT_CONFIG checkout entry has limit of 2 AND a maxPrice of 30 (or equivalent low-price constraint)"
},
{
"name": "Placement types correct",
"max_score": 8,
"description": "PLACEMENT_CONFIG sets type 'frequently-bought-together' for pdp and 'cross-sell' for cart, checkout, and post-purchase"
},
{
"name": "Checkout risk documented",
"max_score": 8,
"description": "api-notes.md explains that checkout shows fewer, low-cost items to reduce cart abandonment risk"
}
]
}
Product Recommendation API for a Multi-Page E-Commerce Store
Problem/Feature Description
A fashion accessories brand is launching a recommendation engine to grow their average order value. They sell across four main surfaces: product detail pages (PDP), a cart drawer, a checkout summary panel, and a post-purchase confirmation page. The marketing team wants different recommendation types and limits on each surface, with a particular concern about the checkout page — past experiments have shown that showing too many suggestions there increases cart abandonment.
The team already has a product_affinities table populated by a nightly job, and a manual_recommendations table where merchandisers can pin specific product pairings (e.g., new arrivals that have no purchase history yet). They need a single getRecommendations API function plus a PLACEMENT_CONFIG object that drives what each page surface requests. The API must gracefully handle cases where affinity data is sparse (new store, new products) and must never surface out-of-stock items or recommend a product to itself. For upsell recommendations, the candidates must come from the same category and sit within a specific price band relative to the source product.
Output Specification
Write recommendations.ts containing:
- A
getRecommendations(req)function - A
getUpsellCandidates(productId, limit?)function - A
PLACEMENT_CONFIGconstant
Write recommendations.test.ts with at least 5 unit tests covering key behaviors of the functions above (mock the db layer as needed).
Also write a short api-notes.md describing:
- The layering strategy used when combining recommendation sources
- How the checkout page configuration differs from other placements and why
{
"context": "Tests whether the agent uses SWR with revalidateOnFocus disabled, implements the responsive grid layout, tracks the correct analytics fields on add-to-cart, assigns A/B variants stably by customer ID hash, and documents the attribution rationale.",
"type": "weighted_checklist",
"checklist": [
{
"name": "SWR used",
"max_score": 10,
"description": "CrossSellWidget.tsx uses useSWR (from the 'swr' package) to fetch recommendation data — not useEffect+fetch or another data-fetching approach"
},
{
"name": "revalidateOnFocus disabled",
"max_score": 10,
"description": "The useSWR call includes { revalidateOnFocus: false } in its options"
},
{
"name": "Grid layout classes",
"max_score": 8,
"description": "The product grid uses Tailwind classes grid, grid-cols-2, md:grid-cols-4, and gap-4 (or equivalent responsive 2→4 column layout)"
},
{
"name": "Empty/loading guard",
"max_score": 8,
"description": "Widget returns null (or equivalent no-render) when isLoading is true or when there are no recommendation results"
},
{
"name": "Analytics event name",
"max_score": 8,
"description": "The analytics tracking call uses the event name 'recommendation_added_to_cart' (exact string)"
},
{
"name": "Analytics recommendedProductId",
"max_score": 6,
"description": "The analytics event payload includes recommendedProductId (the ID of the product being added)"
},
{
"name": "Analytics sourceProductIds",
"max_score": 6,
"description": "The analytics event payload includes sourceProductIds (the product IDs that generated the recommendation context)"
},
{
"name": "Analytics position",
"max_score": 6,
"description": "The analytics event payload includes position (the 1-based index of the product card in the widget)"
},
{
"name": "Analytics algorithm",
"max_score": 6,
"description": "The analytics event payload includes the algorithm/reason field (the source of the recommendation: affinity, manual, trending, etc.)"
},
{
"name": "A/B customer-level hash",
"max_score": 10,
"description": "ab-testing.ts assigns variants using a hash derived from the customerId string — not a random value, session ID, or timestamp"
},
{
"name": "A/B stable assignment",
"max_score": 10,
"description": "widget-notes.md explains that variant assignment is based on customer ID so the same customer always receives the same variant across sessions/devices"
},
{
"name": "Attribution explained",
"max_score": 8,
"description": "widget-notes.md explains that the tracked fields allow measuring incremental AOV or attributing revenue to specific algorithms/placements"
},
{
"name": "Analytics type field",
"max_score": 4,
"description": "The analytics event payload includes the recommendation type (cross-sell, upsell, or frequently-bought-together)"
}
]
}
Recommendation Widget with Experiment Tracking
Problem/Feature Description
A home goods retailer has a working recommendation API and wants to roll out a React-based recommendation widget across their storefront. The growth team also wants to run a structured experiment comparing different recommendation algorithms (affinity-based vs. trending) against a control group. To make the experiment trustworthy, they need consistent assignment — a customer who sees "affinity" recommendations on Monday should still be in the affinity group on Friday, regardless of which device or browser session they use.
The data team has a separate concern: when a customer adds a recommended product to their cart, they need to know which algorithm drove that add, where on the page the widget was positioned, and which products were shown alongside it. This attribution data is essential to calculate whether the recommendation program is actually growing incremental revenue rather than just capturing purchases that would have happened anyway.
Output Specification
Produce the following files:
1. CrossSellWidget.tsx — a React component that fetches and renders recommendations 2. ab-testing.ts — a module with a function that assigns a customer to an experiment variant 3. analytics.ts — a module with a function that fires the add-to-cart tracking event 4. widget-notes.md — brief notes covering:
- How the widget handles loading states and empty results
- What data is captured in the add-to-cart event and why
- How A/B variant assignment works and why it's designed that way
Input Files
No additional files are provided. Use standard React patterns and assume SWR is available as a dependency.
{
"name": "finsi/cross-sell-upsell-engine",
"version": "0.1.0",
"summary": "Recommend complementary and premium products at checkout, in cart, and post-purchase using purchase patterns, browsing history, and margin optimization",
"skills": {
"cross-sell-upsell-engine": {
"path": "SKILL.md"
}
}
}