React Cart for 1C-Bitrix: Development and Integration

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Showing 1 of 1All 1626 services
React Cart for 1C-Bitrix: Development and Integration
Medium
~1-2 weeks
Frequently Asked Questions

Our competencies:

Development stages

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1356
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Website development for FIXPER company
    943
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    693
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    828
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    731
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    1072

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

  1. Analysis and audit. We study the current cart implementation, identify bottlenecks.
  2. API design. Agree on endpoint structure and data format.
  3. React component development. Implement store, components, and backend integration.
  4. Integration with 1C-Bitrix. Configure routing, synchronization, and error handling.
  5. Testing. Verify scenarios: add, delete, coupons, authorization, mobile devices.
  6. 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

What Does React Development for 1C-Bitrix Provide and When Is It Needed?

A catalog with 30,000 SKUs and a faceted filter — the standard Bitrix template loads the page in 3 seconds. A B2B cabinet with personalized discounts — price recalculation with every filter change. The bottleneck is not the data but the monolithic architecture: each block calls REST separately, 6–8 sequential requests of 200–500 ms result in a total delay of 2–3 seconds. React solves this radically: component model, virtual DOM, and library ecosystem turn a slow interface into a responsive application. We are a certified 1C-Bitrix partner with 10+ years of experience and over 500 completed projects. Order an audit — we will evaluate your project in 1–2 days and show cases similar to yours.

Architectural Approaches

SPA on React + REST API Bitrix (BX.rest)

The React application lives separately, accessing /rest/ or custom endpoints via CRestServer. Maximum control, but also maximum work.

  • Client-side routing with React Router — transitions without reload, but on F5 you need a catch-all on Nginx: try_files $uri /index.html
  • Optimistic updates: cart updates instantly, sale.basket.update flies in the background. On error, roll back state and show a toast
  • Frontend deploys to CDN independent of Bitrix — updated a button without touching the backend

SSR with Hydration — When Yandex Doesn't See SPA

Yandex has learned to render JS, but not perfectly; Googlebot is better, but still not 100%. Server-side rendering of React components via Node.js solves the problem radically: the bot receives ready HTML, the user gets an interactive application after hydration. FCP drops below one second on normal hosting, og:title and og:image work for social networks. The complexity is needing a Node.js process alongside Apache/Nginx serving Bitrix: two runtimes, two deploys, two log sets. Bitrix cache (CPHPCache, Composite) can be used to warm data that later goes to SSR.

Headless Bitrix — Admin Panel for Content Managers, React for Visitors

Content manager logs into /bitrix/admin/, edits infoblocks. The visitor sees a React application that retrieves data via API. One backend serves the site, mobile app, and Telegram bot. Scaling: React bundle on CloudFront/CDN, Bitrix on a single server. With 50,000 unique visitors, the frontend does not directly load the backend. Pitfall: the standard Bitrix visual editor (BXEditor) stops working for visitors — content managers will have to work only through the admin panel.

Stack and Component Architecture

Stack We Actually Use

Technology Why It's Used
React 18+ Suspense, useTransition — UI does not block during heavy catalog updates
TypeScript Typing Bitrix API responses — IBlockElement, BasketItem, Order. Without it, refactoring is Russian roulette
Vite HMR in 50 ms vs 3–5 s for webpack. On a project with 200 components, the difference is enormous
React Query useQuery(['catalog', sectionId]) — automatic cache, revalidation, retry on 503 from overloaded Bitrix
React Hook Form + Zod Order form: 15–20 fields, conditional validation (legal entity — one set, individual — another). RHF does not re-render form on every keystroke
Tailwind CSS Utility classes — no fighting with cascades from Bitrix's template_styles.css
Radix UI / Shadcn Accessible primitives with ARIA out of the box

How We Build Components and Type Data

Every project starts with a design system — otherwise, by the third month, three developers will write three different button components. Typography, colors, spacing — via CSS variables and Tailwind config. Forms: inputs with masks (phone, INN), searchable selects, file upload with preview and MIME validation. Product card is a separate story: price with discounts from CCatalogProduct::GetOptimalPrice(), labels "Hit"/"New" from infoblock properties, "Add to cart" button with loading/success/error states. Tables with virtualization (react-window) for price lists of 5000+ rows.

We type everything that comes from Bitrix. The REST API returns string where you'd expect number, "Y"/"N" instead of boolean, and null instead of empty array. A Zod schema on input parses and transforms — components get proper types.

// Real type of CIBlockElement via REST — surprises everywhere
interface BitrixProduct {
  ID: string;          // yes, string, not number
  ACTIVE: "Y" | "N";  // not boolean
  PRICE: string;       // also string
  QUANTITY: string;    // and this is string
}

Performance and API Integration

Aggregating endpoints are key. One ajax.php or custom controller on \Bitrix\Main\Engine\Controller gathers catalog data, filters, cart, and user in a single request. React Query caches the response, and a second visit returns from cache with staleTime — load time reduces by 60% already on the second load.

How Does React Improve Core Web Vitals?

  • LCP < 2.5 s — lazy loading images via loading="lazy", inline critical CSS, preload LCP image via <link rel="preload">
  • INP (replaces FID) < 200 ms — useTransition for heavy filtering, useDeferredValue for search input
  • CLS < 0.1 — fixed sizes for skeletons and images. Skeleton placeholders instead of spinners

Virtualization is not optional, but necessary. A catalog with faceted filter may return 500 products per page. React-window or react-virtuoso render only the visible 20–30 cards — DOM does not bloat, scrolling is smooth.

REST and Custom Controllers

Out of the box via /rest/: infoblocks (iblock.element.get), cart (sale.basket.*), orders (sale.order.*), users (user.*). For a simple catalog, that's enough. But 70% of tasks require custom endpoints. \Bitrix\Main\Engine\Controller is the standard way to create your own endpoints in D7. Write a controller, register via registerAction, get endpoint with CSRF protection and authorization out of the box.

  • Aggregation: one request = catalog data + filters + cart + user
  • WebSocket via Bitrix Push & Pull (CPullStack::AddByTag) — order status updates in real time, no polling
  • GraphQL middleware (webonyx/graphql-php) on top of D7 ORM — frontend requests exactly the fields it needs. Mobile traffic savings up to 40%

Projects, Timelines, and What's Included

Typical Projects We Have Already Done

  • Online store with 30,000 SKUs and faceted filter via \Bitrix\Iblock\PropertyIndex\Facet — SPA, React Query, catalog virtualization
  • B2B cabinet: personalized prices from CCatalogGroup, reconciliation statements from 1C via \Bitrix\Sale\Compatible\OrderCompatibility, order history with filtering
  • Corporate portal: dashboards on Recharts, real-time via Push & Pull, integration with internal APIs through middleware
  • Marketplace: two React applications (buyer + seller), common backend, data separation via CUser::GetUserGroup()

Timelines and What's Included

Project Type Timeline
Landing page on React + Bitrix 2–4 weeks
SPA online store 8–16 weeks
Corporate portal 10–20 weeks
Gradual frontend migration to React 6–12 weeks
  • Audit of current Bitrix code and architecture
  • Design of API layer (REST / custom controllers / GraphQL)
  • Development of design system and components
  • CI/CD setup (deploy React bundle independently of Bitrix)
  • Documentation of endpoints and types (Swagger / TypeScript types)
  • Transfer of access to server, admin panel, repository
  • Training content managers to work through the admin panel
  • Warranty support for 2 months after delivery

Exact numbers after scope analysis. Assessment is phased, with a fixed budget for each sprint.

How We Implement React in a Project

  1. Audit existing code — find bottlenecks: redundant requests, outdated templates, suboptimal caches.
  2. Design API layer — determine which endpoints are needed, design aggregators or GraphQL.
  3. Develop design system — create components (buttons, forms, cards) based on mockups or UX recommendations.
  4. Integrate with Bitrix via chosen approach (SPA, SSR, or Headless) — set up rendering and routing.
  5. Test and deploy — launch a pilot section (e.g., catalog), measure Core Web Vitals, upon approval expand.
Typical Mistakes When Implementing React in Bitrix
  • Ignoring Bitrix caching — React Query may conflict with composite cache if tagged caching is not configured.
  • Lack of error handling from REST — on a 500 error, the interface may "freeze". Need a global handler with fallback UI.
  • Too many micro-components — each small widget calls API. Better to aggregate data in a single request.
  • Wrong hydration order in SSR — data from the server must exactly match the client's initial state, otherwise React hydration errors.

Why React, Not Vue or Bitrix Templates

  • Ecosystem. For any UI task, there is a ready library: tables, charts, drag-and-drop, virtualization. For Vue, the choice is narrower; for Bitrix templates, almost absent.
  • Talent pool. Finding a React developer is three times easier than a Bitrix templater who knows D7 and template.php.
  • React Native. Components are reused in mobile app — not one-to-one, but business logic and types are shared.
  • Gradual adoption. Start with one section (/catalog/) on React, keep the rest on Bitrix templates. component_epilog.php loads the React bundle, data is passed via window.__INITIAL_DATA__.

1C-Bitrix + React is not a theoretical architecture but a working combination that already serves catalogs with tens of thousands of SKUs and B2B cabinets with heavy business logic. Learn more about React and 1C-Bitrix. Get a consultation — we will send you cases similar to your project. Contact us to discuss details. We implement turnkey with a guarantee of results.