Cart page optimization to reduce abandoned carts

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Showing 1 of 1 servicesAll 2065 services
Cart page optimization to reduce abandoned carts
Medium
~2-3 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

Cart Page Optimization for Reducing Abandoned Carts

According to Baymard Institute, the average abandoned cart rate is 70.19%. Half of these losses are related to technical and UX issues that are solved at the development level. The remaining half is related to intent (the user was just comparing prices), and email retargeting helps here.

Main Reasons for Abandoned Carts (Baymard Data)

Reason % of Users
Unexpected costs (shipping, taxes) 48%
Mandatory registration 26%
Slow loading / technical errors 17%
Distrust when entering card data 19%
Complicated checkout process 18%

The first point is most critical. Hidden shipping costs that appear only at checkout are the main conversion killer.

Display Shipping Cost in Cart

Shipping should be calculated right in the cart, before checkout. Widget "Calculate Shipping" with city/zip field:

const ShippingCalculator: React.FC<{ cartTotal: number }> = ({ cartTotal }) => {
  const [zip, setZip] = useState('');
  const [rates, setRates] = useState<ShippingRate[]>([]);

  const calculate = async () => {
    const result = await api.post('/shipping/rates', {
      zip,
      items: cartItems,
      total: cartTotal,
    });
    setRates(result.data);
  };

  return (
    <div className="shipping-calculator">
      <input
        placeholder="Enter zip or city"
        value={zip}
        onChange={e => setZip(e.target.value)}
        onBlur={calculate}
      />
      {rates.map(rate => (
        <ShippingOption key={rate.id} rate={rate} />
      ))}
      {cartTotal >= FREE_SHIPPING_THRESHOLD && (
        <FreeShippingBadge />
      )}
    </div>
  );
};

Free shipping at amount X — powerful incentive to buy more. Progress bar to threshold:

const threshold = 5000; // rubles
const remaining = threshold - cartTotal;

<div className="free-shipping-progress">
  <progress value={cartTotal} max={threshold} />
  {remaining > 0
    ? <span>Add {remaining} ₽ for free shipping</span>
    : <span>Free shipping!</span>
  }
</div>

Such a progress bar increases average order value by 10–30% for users who initially planned to buy less.

Saving Cart

Cart should be saved between sessions. For logged-in users — in database, for guests — in localStorage with sync on login.

// Cart model (for logged-in)
class CartItem extends Model {
    protected $fillable = ['cart_id', 'product_id', 'variant_id', 'quantity'];
}

// On login — merge guest cart with server
public function mergeGuestCart(array $guestItems): void {
    foreach ($guestItems as $item) {
        $this->items()->updateOrCreate(
            ['product_id' => $item['product_id'], 'variant_id' => $item['variant_id']],
            ['quantity' => DB::raw("quantity + {$item['quantity']}")],
        );
    }
}

Abandoned Cart Emails

Email 1 hour after user left with cart — one of the highest-converting email triggers (recovery rate 5–15%).

Trigger: when adding to cart, create record with abandoned_at = NULL. If user doesn't complete order in 60 minutes and last activity > 60 minutes ago — mark cart as abandoned.

// Scheduled Job (every 15 minutes)
Cart::query()
    ->where('updated_at', '<', now()->subMinutes(60))
    ->whereNull('order_id') // incomplete order
    ->whereNull('abandoned_email_sent_at')
    ->with('user', 'items.product')
    ->chunk(100, function ($carts) {
        foreach ($carts as $cart) {
            SendAbandonedCartEmail::dispatch($cart);
            $cart->update(['abandoned_email_sent_at' => now()]);
        }
    });

Email contains:

  • Product list with photos and prices
  • "Return to Order" button (link with UTM tag)
  • Social proof (reviews for cart products)

Email series: 1 hour, 24 hours, 72 hours. Third email may include discount — but only if first two didn't work.

Upsell and Cross-Sell in Cart

Recommendations must be relevant, not random. Algorithms:

"Frequently Bought Together" — based on co-occurrence in order history:

SELECT
    oi2.product_id,
    COUNT(*) AS together_count
FROM order_items oi1
JOIN order_items oi2 ON oi1.order_id = oi2.order_id AND oi1.product_id != oi2.product_id
WHERE oi1.product_id = $current_product_id
GROUP BY oi2.product_id
ORDER BY together_count DESC
LIMIT 5;

"Upgrade Your Set" — if budget option in cart, offer premium with price difference. Upgrade conversion — 8–15%.

Minimize Steps to Checkout

"Checkout" button must be visible without scrolling. On mobile — fixed panel at bottom of screen:

@media (max-width: 768px) {
  .cart-checkout-btn {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 100;
    padding: 16px;
    background: white;
    box-shadow: 0 -2px 8px rgba(0,0,0,0.1);
  }
}

Product Availability Indicators

If product is running out, show in cart:

{item.stock_count <= 5 && (
  <span className="low-stock-warning">
    Only {item.stock_count} left
  </span>
)}

This creates soft pressure without looking manipulative — just useful information.

Technical Optimizations

Optimistic UI when changing quantity. User clicks "+" — quantity changes instantly in UI, API request goes in background. If request fails — rollback:

const updateQuantity = async (itemId: number, newQty: number) => {
  const prevQty = item.quantity;
  setItems(prev => prev.map(i => i.id === itemId ? {...i, quantity: newQty} : i));

  try {
    await api.patch(`/cart/items/${itemId}`, { quantity: newQty });
  } catch {
    setItems(prev => prev.map(i => i.id === itemId ? {...i, quantity: prevQty} : i));
    toast.error('Could not update quantity');
  }
};

Real-time total recalculation without page reload — baseline requirement, but important that it's instant.

Timeline

Task Time
Shipping calculator + progress bar 1 day
Cart persistence for guests 1 day
Abandoned cart emails 1–2 days
Recommendations (cross-sell) 1–2 days

Total: 3–5 business days.