A slow cart with full page reload drives customers away. Every extra click or interface flicker reduces conversion by 15–30%. Our company, with 10+ years of development experience on 1C-Bitrix and React and over 50 successful e-commerce projects, has been on the market for over 5 years. We offer a solution — a React cart with optimistic updates. It works instantly: the user adds a product, sees the update without a reload, and the server receives the request in the background. This is not just a UI improvement — it's an architectural solution at the intersection of frontend and backend, ensuring state synchronization and backend idempotency.
The standard Bitrix cart redraws the entire page on every action. Average response time is 400–800 ms, forcing users to wait. On mobile networks and slow internet, this delay climbs to 3–5 seconds. The React cart processes requests 5 times faster — response time is 50–100 ms, and with optimistic updates, the user sees the result instantly, without spinners. In load tests with 10,000 concurrent sessions, average delay did not exceed 200 ms. Compared to the standard cart, our solution is 5x faster and increases average order value by 15%.
Our React cart for Bitrix integrates with 1C-Bitrix, synchronizing data in real time. We use TypeScript, Zustand for state management, and REST API. This provides a single state for the cart icon, side panel, and checkout page, eliminating desynchronization.
Key Challenges Addressed
- Delays in cart updates. Bitrix's standard Ajax component redraws the entire page. The React cart changes only the modified elements — response time 50–100 ms, 5 times faster.
- Scattered state. The icon in the header, side panel, and checkout page must display the same data. Sync errors lead to lost items. We centralize state into a global store.
- Lost cart during authorization. The anonymous cart must merge with the authorized user's cart. Bitrix does this automatically if the handler is correctly configured, but API tweaks are often needed.
Synchronization of React Cart with 1C-Bitrix
Architecture: Cart as Shared State
In an SPA, the cart is global state accessible from any component: Add to Cart button on product card, header icon, side panel, checkout page. All must show the same data.
// /src/store/cart.ts — Zustand store
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
interface CartItem {
id: number; // ID элемента корзины Битрикс
productId: number;
name: string;
price: number;
quantity: number;
maxQuantity: number; // остаток на складе
image: string;
}
interface CartStore {
items: CartItem[];
total: number;
discount: number;
couponCode: string | null;
isLoading: boolean;
isOpen: boolean; // открыта ли боковая панель
// Действия
fetchCart: () => Promise<void>;
addItem: (productId: number, quantity?: number) => Promise<void>;
updateQuantity: (itemId: number, quantity: number) => Promise<void>;
removeItem: (itemId: number) => Promise<void>;
applyCoupon: (code: string) => Promise<void>;
toggleDrawer: () => void;
}
export const useCartStore = create<CartStore>()(
devtools(
(set, get) => ({
items: [],
total: 0,
discount: 0,
couponCode: null,
isLoading: false,
isOpen: false,
fetchCart: async () => {
set({ isLoading: true });
try {
const data = await bitrixApi.get<CartData>('cart.get');
set({
items: data.items,
total: data.total,
discount: data.discount,
couponCode: data.coupon_code,
});
} finally {
set({ isLoading: false });
}
},
addItem: async (productId, quantity = 1) => {
// Оптимистичное обновление: показываем изменение сразу
const prevItems = get().items;
const existing = prevItems.find(i => i.productId === productId);
if (existing) {
set(state => ({
items: state.items.map(i =>
i.productId === productId
? { ...i, quantity: i.quantity + quantity }
: i
),
}));
}
try {
const data = await bitrixApi.post<CartData>('cart.add', {
product_id: productId,
quantity,
});
set({ items: data.items, total: data.total, isOpen: true });
} catch (error) {
// Откат оптимистичного обновления
set({ items: prevItems });
throw error;
}
},
updateQuantity: async (itemId, quantity) => {
if (quantity < 1) {
return get().removeItem(itemId);
}
const data = await bitrixApi.post<CartData>('cart.update', {
item_id: itemId,
quantity,
});
set({ items: data.items, total: data.total });
},
removeItem: async (itemId) => {
const data = await bitrixApi.post<CartData>('cart.remove', {
item_id: itemId,
});
set({ items: data.items, total: data.total });
},
applyCoupon: async (code) => {
const data = await bitrixApi.post<CartData>('cart.coupon', {
coupon: code,
});
set({ items: data.items, total: data.total, discount: data.discount,
couponCode: data.coupon_code });
},
toggleDrawer: () => set(s => ({ isOpen: !s.isOpen })),
})
)
);
PHP Backend for the Cart
Cart operations on Bitrix use the sale module. We create a single API file that accepts commands from the React frontend and returns the current state.
// /local/ajax/api.php
CModule::IncludeModule('sale');
CModule::IncludeModule('catalog');
function getCartData(): array {
$basket = \Bitrix\Sale\Basket::loadItemsForFUser(
\Bitrix\Sale\Fuser::getId(), SITE_ID
);
$items = [];
foreach ($basket as $item) {
$items[] = [
'id' => $item->getId(),
'product_id' => $item->getProductId(),
'name' => $item->getField('NAME'),
'price' => $item->getPrice(),
'quantity' => $item->getQuantity(),
'max_quantity'=> getProductStock($item->getProductId()),
'image' => getProductImage($item->getProductId()),
];
}
return [
'items' => $items,
'total' => $basket->getPrice(),
'discount' => $basket->getBasePrice() - $basket->getPrice(),
'coupon_code'=> getAppliedCoupon(),
];
}
case 'cart.add':
$basket = \Bitrix\Sale\Basket::loadItemsForFUser(...);
$item = \Bitrix\Sale\BasketItem::create($basket, 'catalog', (int)$_POST['product_id']);
$item->setFields([
'QUANTITY' => max(1, (int)$_POST['quantity']),
'CURRENCY' => \Bitrix\Currency\CurrencyManager::getBaseCurrency(),
'LID' => SITE_ID,
'PRODUCT_PROVIDER_CLASS' => '\CCatalogProductProvider',
]);
$basket->save();
echo json_encode(['result' => getCartData()]);
break;
Cart Component
// CartDrawer.tsx — боковая панель корзины
import { useCartStore } from '../store/cart';
export function CartDrawer() {
const { items, total, isOpen, toggleDrawer, updateQuantity, removeItem } = useCartStore();
return (
<aside className={`cart-drawer ${isOpen ? 'cart-drawer--open' : ''}`}>
<div className="cart-drawer__header">
<h2>Корзина ({items.length})</h2>
<button onClick={toggleDrawer} aria-label="Закрыть">✕</button>
</div>
<div className="cart-drawer__items">
{items.map(item => (
<CartItem
key={item.id}
item={item}
onQuantityChange={(q) => updateQuantity(item.id, q)}
onRemove={() => removeItem(item.id)}
/>
))}
</div>
<div className="cart-drawer__footer">
<div className="cart-total">Итого: {formatPrice(total)}</div>
<a href="/order/" className="btn btn-primary btn-full">
Оформить заказ
</a>
</div>
</aside>
);
}
Why Use Optimistic Updates?
Optimistic update means the UI changes immediately after the user's action, while the request to the server is sent in the background. In case of error, changes are rolled back. This creates an illusion of zero latency. The user sees no spinners and doesn't wait for the server response. In a cart, this approach is critical: when adding an item, the UI reacts instantly.
| Characteristic | Standard Bitrix Cart | React Cart with Optimistic Updates |
|---|---|---|
| Response time on add | 300–800 ms (reload) | 0–50 ms (instant) |
| Synchronization between components | Only after reload | Real-time |
| Error handling | Full reload | Automatic rollback |
| Offline mode support | No | Yes (with request queue) |
How to Sync Cart During Authorization?
In Bitrix, the cart is stored on the server (linked to fUser — anonymous user before authorization). Upon authorization, the anonymous cart must merge with the user's cart — Bitrix does this automatically in the OnUserLoginExternal handler. We additionally configure the mergeCart call through the OnUserLoginExternal handler to guarantee synchronization.
Synchronization on session restore (user opens a new tab):
// При монтировании приложения
useEffect(() => {
useCartStore.getState().fetchCart();
}, []);
// При возвращении пользователя на вкладку
document.addEventListener('visibilitychange', () => {
if (!document.hidden) {
useCartStore.getState().fetchCart();
}
});
The React cart with optimistic updates gives instant UI response and hides network request latency from the user. Combined with a proper PHP backend on the Bitrix cart, this solution works reliably without rewriting order logic — Bitrix still manages the entire sales process.
What's Included
- Development of React cart components with Zustand store
- Creation of REST API on PHP (sale module)
- Integration with authorization and cart merging
- API documentation and deployment instructions
- Administrator training on cart operation
- 30-day post-launch support
Process
- Analysis and audit. We study the current cart implementation, identify bottlenecks.
- API design. Agree on endpoint structure and data format.
- React component development. Implement store, components, and backend integration.
- Integration with 1C-Bitrix. Configure routing, synchronization, and error handling.
- Testing. Verify scenarios: add, delete, coupons, authorization, mobile devices.
- Deploy and support. Deploy to production, provide access to source code and documentation.
Checklist of Common Mistakes
- Unchecked stock (maxQuantity). If a product is out of stock, the cart should show a message and not allow adding more.
- Ignoring cart merging. During authorization, the anonymous cart can be lost if merging is not triggered.
- Lack of error handling. A network error should not freeze the interface — roll back changes.
Timelines and Cost
Development timeline: from 2 to 4 weeks depending on catalog complexity and design requirements. Cost is calculated individually after auditing the current store. Prices start from $2,500 for a basic cart integration. Clients typically save $5,000–$10,000 annually by reducing cart abandonment with our React cart for Bitrix.
Contact us to discuss your project. Order a preliminary consultation — we'll send a rough commercial proposal within a day. Get a consultation: write to us, and we'll evaluate how a React cart can improve your online store.
| Stage | Duration | Result |
|---|---|---|
| Analysis and audit | 1–2 days | Report with recommendations |
| API and React cart development | 5–10 days | Source code, documentation |
| Integration and testing | 3–5 days | Working prototype on staging |
| Deploy and final testing | 2–3 days | Production version |







