Cross Sell and Upsell at Checkout: What Actually Lifts Basket Value
I have lost count of the number of shops I have audited where the owner told me they 'do cross selling' and what they meant was a widget at the bottom of the product page showing four random items from the same category. Nobody had looked at whether anyone clicked it. Nobody knew whether the people who did click it spent more in the end or just wandered off. The widget came with the theme, so it stayed. That is the state of cross selling in most small and medium sized shops, and it is a shame, because getting more out of the customers you already have is the cheapest growth there is. You have already paid for the click. You have already won the trust. The only question is whether the shop is built to make the second item as easy as the first. This article is about how to do that properly: what the different mechanics actually are, where in the journey each one earns its place, the numbers that tell you whether it is working, the psychology that is real and the psychology that gets you an Abmahnung, and how I build it into Solidus. I am using the demo shops I am building as the running example, because I would rather show you the wiring than talk about it in the abstract.
The widget nobody measures
I have lost count of the number of shops I have audited where the owner told me they "do cross selling" and what they meant was a widget at the bottom of the product page showing four random items from the same category. Nobody had looked at whether anyone clicked it. Nobody knew whether the people who did click it spent more in the end or just wandered off. The widget came with the theme, so it stayed.
That is the state of cross selling in most small and medium sized shops, and it is a shame, because getting more out of the customers you already have is the cheapest growth there is. You have already paid for the click. You have already won the trust. The only question is whether the shop is built to make the second item as easy as the first.
This article is about how to do that properly: what the different mechanics actually are, where in the journey each one earns its place, the numbers that tell you whether it is working, the psychology that is real and the psychology that gets you an Abmahnung, and how I build it into Solidus. I am using the demo shops I am building as the running example, because I would rather show you the wiring than talk about it in the abstract.
Four mechanics, not one
People use "cross sell" and "upsell" interchangeably, and then they wonder why their upsell is not converting when what they built was a cross sell in the wrong place. So let me be pedantic for a moment.
Cross sell is a different product that goes with the one being bought. Mattress with the bed frame. Batteries with the torch. Gift wrap with the hamper. The customer's decision is "do I also want this", and the answer depends entirely on whether the suggestion is genuinely complementary.
Upsell is a better version of the same product. The larger hamper. The oak frame instead of the pine one. The two year subscription instead of the monthly. The decision is "do I want more of what I already decided I want", which is a much easier yes if the difference in value is legible and the difference in price is moderate.
Bundles are a fixed combination sold as one item at one price, usually with a small saving. The build your own hamper on my gift store is a bundle with a twist: the customer picks the contents, the container and the price are fixed.
Add ons are the small things attached at the last moment. Gift message, express delivery, extended warranty, assembly. Low price, high margin, minimal thought.
Each of these has a natural home in the journey and a natural failure mode when it is put in the wrong one. An upsell at the checkout is an interruption. A cross sell in the post purchase email is a second order. A bundle on the product page is a decision aid. An add on in the cart is a tick box. Get the placement right and the mechanic does most of the work for you.
Where in the journey each one belongs
I think about the journey in five places, and I think about what the customer is doing in each one, because the mechanic has to fit the moment.
The product page. The customer is evaluating. They have not decided yet. This is the home of the upsell (show me the better version side by side) and of the curated cross sell that helps the decision rather than distracting from it. On the furniture store I am building, the bed frame page has a "complete the bed" panel that shows a mattress and bedding in the matching size. It is not a random carousel. It re filters when the customer switches from 140 to 180 centimetres, because a mattress in the wrong size is not a cross sell, it is a return. That panel is doing two jobs: it lifts the basket, and it answers the question "will this fit" before the customer has to ask it.
The add to cart moment. The customer has just decided. This is the highest intent moment in the whole shop and most stores waste it on a green flash message. I replace the flash with a drawer: here is what you added, here is your total, here are two things that go with it, one click to add each, and a clear button to keep shopping or check out. Nothing else. On the gift store the drawer after adding a hamper offers a gift card and a bottle upgrade. The evidence across the shops I have worked on is that the drawer is the single highest return placement, because the customer is in a "yes" mood and the offer is small.
The cart. The customer is reviewing. This is where thresholds live ("add 12 euros for free delivery"), where the last curated cross sell can sit, and where add ons like gift wrap belong. It is not where you try to change their mind about the main product.
The checkout. The customer is paying. Leave them alone. Every step you add here costs you more in abandonment than it makes in attachment. The only things I allow in checkout are add ons that are genuinely about fulfilment: express delivery, a delivery slot, a gift message. One tick box each, unticked by default, which is now the law in any case, and I will come back to that.
Post purchase. The order is placed and the customer's guard is down. This is where the real upsell and cross sell for the next order happens: the confirmation page with a one click add before the order ships, the email a week later with the thing that goes with what they bought, the reorder prompt when a consumable is about to run out. I would put more effort here than in checkout, every time.
The maths: why average order value lies to you
Every cross selling case study leads with average order value going up. That number is easy to move and easy to misread, and it is not the number I want you to watch.
Here is the problem. Suppose you add an aggressive upsell to the cart and it works: people who buy now spend twenty percent more. But the interruption also puts some people off, and conversion drops from 2.5 percent to 2.2 percent. Your average order value is up nicely. Your revenue per visitor is down. You have made the shop worse and the dashboard says you made it better.
So the first number is revenue per visitor, or per session if that is what your analytics gives you. Conversion rate multiplied by average order value. It moves when either moves, and it cannot be gamed by trading one for the other.
The second number is attach rate: of the orders that saw an offer, how many took it. This is the health of a single placement. A drawer cross sell that attaches at eight percent is good. A product page panel at three percent is normal. A checkout add on at twenty percent is excellent and usually means it is gift wrap in December.
The third, and the one nobody tracks, is margin weighted lift. Cross selling a loss leader is not growth. If the bundle discount eats the margin on the added item, you have added work for the warehouse and nothing for the bank. I put a margin column on every product in Solidus for exactly this reason and I report the lift in contribution, not in revenue. It is a duller number and it is the true one.
A worked example, rounded. A shop with 40,000 visitors a month, 2.5 percent conversion, 85 euros average order, 38 percent gross margin. That is 1,000 orders, 85,000 euros revenue, 32,300 euros contribution. Add a cart drawer cross sell that attaches on nine percent of orders at an average of 14 euros and 45 percent margin, and costs nothing in conversion because it is not in the way. Ninety orders, 1,260 euros revenue, 567 euros contribution per month. Modest. Now add a free shipping threshold nudge that moves the median basket up by six euros on a third of orders: about 2,000 euros revenue and 760 euros contribution. Then a matching size "complete the bed" panel that attaches a mattress on 12 percent of frame orders at 240 euros. If frames are a fifth of orders that is 24 mattresses, 5,760 euros, and at 35 percent margin roughly 2,000 euros contribution.
Together those three placements add around nine thousand euros of revenue and 3,300 euros of contribution a month, which is a bit over ten percent on the top line and a bit over ten percent on the bottom line, from traffic you already had. That is a realistic first quarter. It is not forty percent. Anyone promising forty percent is selling you an app.
Rules or a model
Somewhere around the second meeting, someone will ask about AI recommendations, and I want to give you the honest version of the answer.
There are three ways to decide what to show next to a product.
Curated. A human says "this frame goes with these three mattresses". Highest quality, lowest coverage, and it decays because nobody updates it when the range changes. Right for a catalogue of a few hundred products with a merchandiser who cares.
Rules. Same category, same brand, same size, complements defined at the category level (every bed frame shows mattresses in its size). Medium quality, full coverage, and it stays correct as the catalogue changes because it is derived rather than typed. This is what I build first for every shop, and for most it is where I stop.
Order data. Frequently bought together, computed from real baskets. Better than rules once you have enough orders that the pairs are not noise, which in practice means a few thousand orders across the catalogue and a few dozen per product pair. Below that, "frequently bought together" is one customer in March who bought a candle and a dog lead, and the model will faithfully recommend dog leads to candle buyers until the end of time.
A genuine recommendation model, collaborative filtering or embeddings or whatever the current name is, sits above all three and needs the order volume of a large shop to beat rules by a margin you can measure. I have built them, I wrote about one in my piece on custom recommendations in Solidus, and my honest advice to a shop doing under a couple of thousand orders a month is: do not bother yet. Build the rules, collect the events, and revisit in a year when you have the data to know whether the model earns its keep. What I do use earlier is something lighter: a preference cookie set by a product finder or by browsing, which reorders the same rule based suggestions to match what the customer has shown interest in. That is not machine learning. It is a sort. And it lifts attach rates because the first suggestion is the right one more often.
The psychology that holds up
There is a lot of nonsense written about persuasion in ecommerce, most of it by people selling the widget. I have written about the pricing side and about scarcity and loss aversion before, so here I will keep to what matters for the basket.
Anchoring. The first price a customer sees becomes the reference. Show the larger hamper first and the medium one looks reasonable; show the medium first and the larger looks expensive. On an upsell, the order matters as much as the discount.
The compromise effect. Given three options, people gravitate to the middle. If you sell two sizes, a lot of customers pick the smaller. Add a third, larger one, and the previous "large" becomes the sensible middle and sells more. My gift store's hamper offers are structured exactly this way: a small fixed one, a showstopper, and the one I actually want to sell in between.
Decoys. Related: an option that exists only to make another look good. The 60 euro hamper with six picks next to the 100 euro one with eight and two bottles makes the second look generous. Use sparingly and honestly; the decoy has to be a real product someone could sensibly buy.
Defaults. What is pre selected wins, which is exactly why pre ticking add ons is now illegal, of which more below. The legitimate use is in the upsell: present the recommended size or the recommended bundle as the highlighted option, not the pre added one.
Thresholds. "You are 9 euros from free delivery" is the most reliable basket lifter I know, and it works because it reframes the extra item as saving the delivery charge rather than spending nine euros. Set the threshold a little above your current median basket, not far above it. Too far and it does nothing; too close and you are giving away shipping you were already earning.
Legibility. The under discussed one. An upsell works when the customer can see in a second what the better version gives them. "Oak, 20 year guarantee, 180 euros more" converts. "Premium option" does not. Most failed upsells I have seen failed on copy, not on price.
The psychology that backfires, and is now illegal
The other half of the persuasion literature is manipulation, and in the DACH market it has stopped being a matter of taste.
The German Fair Trading Act was tightened in 2022, the EU's Digital Services Act has applied to every online platform since February 2024 and bans dark patterns outright, and the consumer protection authorities and the Abmahnvereine have been enthusiastically enforcing both. I covered the UX side in the piece on GDPR dark patterns. For the basket specifically:
Pre ticked add ons are out. Since the EU consumer rights rules, any additional payment needs the customer's active consent. A pre ticked "add insurance" box is not consent, and courts have said so repeatedly.
Fake scarcity is out. "Only 2 left" when there are two hundred, or a countdown timer that resets on reload, is a misleading commercial practice and in Germany it is on the blacklist, which means no proof of actual harm is required.
Sneaking is out. Adding an item to the basket that the customer did not choose, even a free sample that later becomes a paid subscription, has been fined.
Confirmshaming ("No thanks, I don't want to save money") is not yet explicitly illegal but it is explicitly covered by the DSA's dark pattern guidance and it is the kind of thing an Abmahnung cites as evidence of an overall deceptive design.
Interruptions are legal and merely stupid. The modal that pops up over the checkout to offer a discount on something else is a conversion killer, and every test I have ever run on one has lost.
The pattern in all of this is simple: anything that works by making the customer choose something they would not have chosen if they understood it, will eventually cost you more than it made. Anything that works by making a genuinely good next choice easy to see and easy to take, keeps working.
Building it into Solidus
Now the part where I show the wiring, because this is where the platform choice from my comparison of the shop systems turns into money. On Shopify all of the above is an app per mechanic, each with a monthly fee, each with its own idea of what your product data looks like, and none of them talking to each other. On Solidus it is a few models and a few views that you own.
Product relationships. Solidus has had a related products extension for years and I generally build my own thin version: a relationship type (complements, upgrade, alternative), a source product, a target product or a target rule, and a position. The rule based part is a scope: "all mattresses whose size matches this frame's size variant". The curated part is a row in the admin. The order data part is a nightly job that computes co purchase counts and writes them into the same table with a lower priority than the curated rows, so a human's judgement always wins over a statistic.
The size matched panel. On the furniture store, the "complete the bed" panel is a small view component that takes the currently selected variant, reads its size option value, and asks the relationship scope for companions with the same size. Switching the variant swaps the panel via a Stimulus controller. No page reload, no app, about a hundred and fifty lines including the tests.
Promotions with thresholds. Free shipping over a threshold, ten percent off the second item in a category, a fixed price bundle: these are all Solidus promotion rules and actions out of the box. The threshold nudge in the cart is a helper that asks the promotion engine how far the current order is from qualifying and renders one sentence. It is the same engine that later applies the discount, so the nudge can never be wrong about the rule.
Bundles as line items. For the build your own hamper I let the container variant carry the line item and store the customer's picks as structured data on it, with a comparison hook so that two hampers with different contents never merge into one line. Stock is decremented for the contents when the order completes. The customer sees one line at one price, the warehouse sees a pick list, and finance sees the margin per component. Doing that on a rented platform is somewhere between hard and impossible.
The add to cart drawer. A Turbo frame that replaces the flash message. It renders the added item, the order total, the two highest priority relationships for that product, and a threshold nudge if one applies. Every add from the drawer is recorded as an event with a source, so I can tell later whether the drawer is earning its place.
Checkout add ons. A single optional step, or rather a section on the delivery step, with genuine fulfilment options: gift message, express, a delivery slot for the truck delivered furniture. Each is a line item or a shipping method, none pre selected, all priced clearly. That is the entire extent of what I put in checkout.
The point of listing this is not the code. It is that all of it is ordinary application code inside a store you own. When the range changes, the rules keep working. When you want to test a different threshold, it is a number in the admin. When you want to know whether the drawer works, the events are in your database, not in an app vendor's dashboard.
Measuring lift so you know rather than hope
This is the part everyone skips and it is the part that separates a shop that grows from a shop that has a lot of widgets.
The problem with measuring cross selling is that the people who take an offer are not a random sample. They were already in a buying mood. If you compare "orders with a cross sell" to "orders without", the first group will always look better and it tells you nothing about whether the offer caused it.
The only honest way is a holdout: a random share of visitors who do not see the placement at all, and a comparison of revenue per visitor between the two groups. Not attach rate, not average order value, revenue per visitor, and ideally contribution per visitor. I wrote up the general method in the complete guide to digital marketing testing methods and the statistics in the piece on significance and error margins, so the short version here.
Sample size. A ten percent lift on a metric with the variance of order value needs thousands of orders per group to be visible, not hundreds. For the shop in my worked example that is a month or two per test. Plan for that. A test you stop after four days because the dashboard looks good is a coin toss you paid for.
One change at a time. If you launch the drawer, the threshold and the panel in the same week you will never know which one did what. Stagger them, or run them as separate holdouts.
Segment after, not before. Look at the overall lift first. Then look at new versus returning, mobile versus desktop, and by category, because a placement that lifts furniture and depresses accessories will net out to nothing and you will switch off something that works.
Watch the second order. The best cross selling I have seen shows up not in the first basket but in the return rate. A customer who bought the mattress with the frame comes back for the bedding. Track revenue per customer over ninety days, not just per session.
In Solidus this is a feature flag on the placement, a cookie that assigns the visitor to a group, and a query. On a rented platform you are at the mercy of whether the app vendor built an experiment mode and whether you trust their numbers.
A realistic playbook
If you have a shop, some traffic, and none of this, here is the order I would do it in, with what I would expect from each.
Week one: the numbers. Revenue per visitor, median basket, gross margin per product, and the top twenty product pairs from your order history. Half the shops I do this for discover a pairing they had no idea about. That is your first curated relationship.
Weeks two to four: rules and the drawer. Category level complements, size or compatibility matching where it applies, and the add to cart drawer showing the top two. Holdout on the drawer. Expect a three to eight percent lift in revenue per visitor from the drawer alone if the suggestions are good.
Month two: the threshold. Free delivery just above the median basket, with the nudge in the cart. Expect two to five percent, and watch the shipping cost line so you are not giving it back.
Month three: the product page panel and the upsell order. Matching companions on the product page, and the three option structure on anything with sizes or tiers. Expect two to four percent, more on categories with natural companions.
Ongoing: post purchase. The confirmation page add, the seven day companion email, the consumable reorder prompt. This is where the second order lives and it is slower to show but it compounds.
Add those up and a shop starting from nothing lands at roughly eight to fifteen percent more revenue per visitor within a quarter, with margin intact because you measured it. After that the gains get smaller and the work gets more specific to your catalogue, which is fine, because by then you will have the data to know where to look.
What I would not do
Install a recommendations app and call it done. Put anything in the checkout that is not about delivery. Pre tick anything. Show a timer. Discount a bundle so deeply that the added item loses money. Report average order value to yourself as the success metric. Run a test for four days. Copy what a shop fifty times your size does, because their order volume makes a model sensible and yours does not yet.
And one more: do not let the mechanics get ahead of the merchandising. The best cross sell in the world cannot fix a range where nothing goes with anything. The furniture store works because a bed frame has a mattress and bedding that belong with it. The gift store works because a hamper has a card and a bottle upgrade. If your catalogue does not have natural companions, the first job is to add some, and no amount of clever placement will substitute for that.
The short version
Getting more from the customers you already have is cheaper than getting new ones, and most shops leave it to a widget nobody measures. Put the upsell on the product page, the cross sell in the drawer and the cart, the add ons in one honest section of the checkout, and the second order in the post purchase flow. Watch revenue per visitor and margin, not average order value. Build rules before models. Stay on the right side of a consumer protection regime that has finally got teeth. And own the store, so that all of this is your code and your data instead of a monthly fee for someone else's idea of what your customers want.