Shopping Cart Development with Vue.js for 1C-Bitrix
The cart is the most UX-sensitive component of any store. The standard bitrix:sale.basket.basket performs a full page reload on every quantity change or item removal. A Vue cart with optimistic updates eliminates this: visually everything happens instantly, while synchronization with the server runs in the background.
Bitrix API for Cart Operations
Core methods of the sale module:
// Add to cart
\Bitrix\Sale\Basket::create(SITE_ID);
$basketItem = $basket->createItem('catalog', $productId);
$basketItem->setFields(['QUANTITY' => 1, 'CURRENCY' => 'RUB']);
$basket->save();
// AJAX controller for Vue:
// POST /basket/add { productId, quantity, props }
// POST /basket/update { itemId, quantity }
// POST /basket/remove { itemId }
// GET /basket — current state
Or via REST API: sale.basketitem.add, sale.basketitem.update, sale.basketitem.delete.
Vue Cart Structure
CartApp.vue — root, reads cartStore
├── CartDrawer.vue — side panel (slide-over)
├── CartIcon.vue — icon with counter in the header
├── CartItem.vue — product row (image, name, quantity, price)
├── CartSummary.vue — total, promo code, checkout button
└── EmptyCart.vue — empty cart placeholder
Pinia Cart Store
export const useCartStore = defineStore('cart', {
state: () => ({
items: [],
totalPrice: 0,
discounts: [],
isLoading: false,
}),
getters: {
itemCount: (state) => state.items.reduce((sum, i) => sum + i.quantity, 0),
},
actions: {
async addItem(productId, quantity = 1) {
// Optimistic update: add locally immediately
this.items.push({ productId, quantity, pending: true });
try {
const result = await cartApi.add(productId, quantity);
// Update with server data (real ID, price)
this.syncFromServer(result.basket);
} catch {
// Rollback on error
this.items = this.items.filter(i => !i.pending);
}
}
}
});
Tab Synchronization
The user has the store open in two tabs. A product is added in one — the cart in the other is stale. The solution:
// Listen for localStorage events (BroadcastChannel — the modern approach)
const channel = new BroadcastChannel('cart-sync');
channel.onmessage = (e) => {
if (e.data.type === 'CART_UPDATED') cartStore.fetchCart();
};
// On cart change:
channel.postMessage({ type: 'CART_UPDATED' });
Real-World Case
An electronics store with the standard Bitrix AJAX cart. Problem: when adding a product via the button on the product card (sale.basket.basket.ajax), the page would "flicker" — the entire cart block in the header was re-rendered. On mobile devices this was perceived as a freeze. A Vue cart with CartIcon (counter updated instantly via Pinia) and CartDrawer (sliding panel opens without page reload) — adding a product is visually instant, the server request runs asynchronously.
Delivery Timelines
| Option | Timeline |
|---|---|
| Drawer cart with basic operations | 4 to 6 business days |
| With promo codes, discounts, saved lists | 8 to 12 business days |
| Integration with Vue catalog and checkout | 3 to 5 additional business days |







