Оптимізація сторінки кошика для зменшення брошених кошиків
За даними Baymard Institute, середній показник брошених кошиків становить 70,19%. Половина цих втрат пов'язана з технічними та UX-проблемами, які вирішуються на рівні розробки. Решта половини пов'язана з намірами (користувач просто порівнював ціни), і тут допомагає email-ретаргетинг.
Основні причини брошених кошиків (дані Baymard)
| Причина | % користувачів |
|---|---|
| Непередбачені витрати (доставка, податки) | 48% |
| Обов'язкова реєстрація | 26% |
| Повільне завантаження / технічні помилки | 17% |
| Недовіра при введенні даних карти | 19% |
| Складний процес оформлення | 18% |
Перший пункт найважливіший. Приховані витрати на доставку, які з'являються тільки при оформленні, — головний убійця конверсії.
Показувати вартість доставки в кошику
Доставка повинна розраховуватися прямо в кошику, до оформлення замовлення. Віджет «Розрахувати доставку» з полем міста/індексу:
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="Введіть індекс або місто"
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>
);
};
Безплатна доставка при сумі X — потужний стимул докупити. Прогрес-бар до порогу:
const threshold = 5000; // гривні
const remaining = threshold - cartTotal;
<div className="free-shipping-progress">
<progress value={cartTotal} max={threshold} />
{remaining > 0
? <span>До безплатної доставки осталося {remaining} ₴</span>
: <span>Безплатна доставка!</span>
}
</div>
Такий прогрес-бар збільшує середній чек на 10–30% у користувачів, які спочатку планували купити менше.
Збереження кошика
Кошик повинен зберігатися між сеансами. Для авторизованих — у базі даних, для гостей — у localStorage з синхронізацією при вході.
// Модель Cart (для авторизованих)
class CartItem extends Model {
protected $fillable = ['cart_id', 'product_id', 'variant_id', 'quantity'];
}
// При вході — merge гостьового кошика з серверним
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']}")],
);
}
}
Email-листи брошених кошиків
Електронна пошта через 1 годину після того, як користувач ходив із кошиком — один із найбільш конвертуючих email-тригерів (коефіцієнт відновлення 5–15%).
Тригер: при додаванні в кошик створюємо запис з abandoned_at = NULL. Якщо користувач не завершив замовлення протягом 60 хвилин і остання активність > 60 хвилин тому — помічаємо кошик як брошений.
// Запланована робота (кожні 15 хвилин)
Cart::query()
->where('updated_at', '<', now()->subMinutes(60))
->whereNull('order_id') // незавершене замовлення
->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 містить:
- Список товарів з фото та цінами
- Кнопку «Повернутися до замовлення» (посилання з UTM-меткою)
- Соціальний доказ (відгуки про товари з кошика)
Серія писем: через 1 годину, через 24 години, через 72 години. Третій лист може містити знижку — але тільки якщо перші два не спрацювали.
Upsell та cross-sell в кошику
Рекомендації повинні бути релевантними, а не випадковими. Алгоритми:
«Часто купують разом» — на основі співвідношення в історії замовлень:
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;
«Улучши комплект» — якщо в кошику бюджетний варіант, пропонуємо преміум з різницею в ціні. Конверсія в апгрейд — 8–15%.
Мінімізація кроків до оформлення
Кнопка «Оформити замовлення» повинна бути видна без прокручування. На мобільних — фіксована панель внизу екрана:
@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);
}
}
Індикатори наявності товару
Якщо товар закінчується, показуємо це в кошику:
{item.stock_count <= 5 && (
<span className="low-stock-warning">
Залишилось {item.stock_count} шт.
</span>
)}
Це створює м'яке тиснення, не виглядаючи як маніпуляція — просто корисна інформація.
Технічні оптимізації
Optimistic UI при змінені кількості. Користувач клацає «+» — кількість миттєво змінюється в UI, запит до API йде в фоні. Якщо запит падає — откатуємо:
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('Не вдалося оновити кількість');
}
};
Пересчет итогов в реальном времени без перезагрузки страницы — базовое требование, но важно чтобы он был мгновенным.
Терміни
| Завдання | Час |
|---|---|
| Калькулятор доставки + прогрес-бар | 1 день |
| Збереження кошика для гостей | 1 день |
| Email-листи брошених кошиків | 1–2 дні |
| Рекомендації (cross-sell) | 1–2 дні |
Разом: 3–5 робочих днів.







