The WooCommerce cart page is one of the highest-intent screens on any store — the customer has already decided to buy something, which makes it the perfect place to suggest complementary or higher-value products. WooCommerce renders upsells on the single product page out of the box via the woocommerce_after_single_product_summary hook, but the cart page has no built-in upsell section. Adding one is a matter of hooking into woocommerce_after_cart_table, querying the products you want to promote, and rendering them with the standard WooCommerce loop template so the styling matches the rest of your store. You can take two approaches: display globally configured featured products regardless of cart contents, or dynamically pull upsell product IDs from the items already in the cart using WC()->cart->get_cart(). The dynamic approach is more relevant to the customer but requires a loop over cart items and a get_upsells() call on each product. Both approaches are shown below. The output is wrapped in a standard woocommerce-column container and uses the wc_get_template_part function to render individual product cards so your theme’s product loop template applies automatically. This saves you from duplicating markup and ensures add-to-cart AJAX still works. Pair this with the programmatic add-to-cart guide if you want to offer one-click upsell buttons that add items silently. Keep the displayed list short — three to four products — to avoid overwhelming the customer before checkout.
Problem: Your WooCommerce store’s cart page has no upsell section and you want to display related products dynamically based on what is already in the cart, without a plugin.
Solution: Hook into woocommerce_after_cart_table, collect upsell IDs from the current cart items, and render them using the standard WooCommerce product loop template:
add_action( 'woocommerce_after_cart_table', 'ha_cart_upsells', 10 );
function ha_cart_upsells() {
// Collect upsell IDs from every product currently in the cart
$upsell_ids = [];
foreach ( WC()->cart->get_cart() as $item ) {
$product = $item['data'];
$upsell_ids = array_merge( $upsell_ids, $product->get_upsell_ids() );
}
// Remove duplicates and IDs already in the cart
$cart_ids = array_column( WC()->cart->get_cart(), 'product_id' );
$upsell_ids = array_unique( array_diff( $upsell_ids, $cart_ids ) );
$upsell_ids = array_slice( $upsell_ids, 0, 4 ); // max 4
if ( empty( $upsell_ids ) ) {
return;
}
$args = [
'post_type' => 'product',
'post__in' => $upsell_ids,
'orderby' => 'post__in',
'posts_per_page' => 4,
'post_status' => 'publish',
];
$loop = new WP_Query( $args );
if ( ! $loop->have_posts() ) {
return;
}
echo '<div class="cart-upsells">';
echo '<h3>' . esc_html__( 'You may also like', 'woocommerce' ) . '</h3>';
echo '<ul class="products">';
while ( $loop->have_posts() ) {
$loop->the_post();
wc_get_template_part( 'content', 'product' );
}
echo '</ul></div>';
wp_reset_postdata();
}
NOTE: The wc_get_template_part( ‘content’, ‘product’ ) call renders each product using your theme’s content-product.php template, so AJAX add-to-cart, sale badges, and rating stars all work without extra code. If your theme does not have that template WooCommerce falls back to its own. Style the .cart-upsells wrapper to match your store grid — typically reusing the .woocommerce ul.products grid rules covers most cases. Also review the product price display guide if you need custom price formatting in the upsell cards.