Every incomplete order is lost money. The default bitrix:sale.order.ajax on jQuery takes 1–2 seconds to update fields, and with 10,000+ items, it can be up to 3 seconds. The user leaves, conversion drops. Customizing a multi-step form, B2B requisites, or delivery date selection on this component is nearly impossible — the code turns into spaghetti of PHP inserts and JavaScript.
We replace sale.order.ajax with a React checkout, turnkey. Our engineers are certified Bitrix developers with 10+ years of experience. After implementation for one client, conversion increased from 62% to 79% within 6 weeks. If you want similar results, contact us — we'll evaluate your project.
Why a React checkout is faster and more flexible
React checkout solves the problem at the architecture level: UI in components, logic in hooks, communication with the server via API. Changing delivery is handled in 200 ms, while the standard component takes over a second. Field validation occurs instantly on blur — the user sees an error immediately, not after clicking the submit button.
How the React checkout architecture works
The checkout is split into two independent layers: the UI layer (React) and business logic (Bitrix on the server). On the front end, a React app manages the form, shows/hides steps, calculates totals in real time. On the server, Bitrix processes the order via \Bitrix\Sale\Order, applies discounts, calculates delivery costs, and checks stock. More about the order system in Bitrix Sale documentation.
The key API method for calculating an order without saving it:
// Calculate totals without saving the order
$order = \Bitrix\Sale\Order::create(SITE_ID, $userId);
$basket = \Bitrix\Sale\Basket::loadSiteBasket(SITE_ID);
$order->setBasket($basket);
// Apply delivery parameters
$shipment = $order->getShipmentCollection()->createItem(
\Bitrix\Sale\Delivery\Services\Manager::getById($deliveryId)
);
$shipment->setFields(['DELIVERY_ID' => $deliveryId, 'CURRENCY' => 'RUB']);
$shipment->calculateDelivery();
// Apply coupon
$order->getDiscountSystem()->calculate();
// Return total without saving (no $order->save() call)
return [
'subtotal' => $basket->getPrice(),
'delivery_price' => $shipment->getPrice(),
'discount' => $order->getDiscountPrice(),
'total' => $order->getPrice(),
];
This endpoint is called on every field change: choosing a delivery service, entering a promo code, changing quantity. React gets the updated figures without a page reload.
How to integrate React checkout with Bitrix
For a complex checkout (3+ steps with validation), we recommend React Hook Form with Zod schemas for validation:
const checkoutSchema = z.object({
contact: z.object({
name: z.string().min(2, 'Enter name'),
phone: z.string().regex(/^\+7\d{10}$/, 'Invalid format'),
email: z.string().email('Invalid email'),
}),
delivery: z.object({
type: z.enum(['courier', 'pickup', 'cdek']),
address: z.string().optional(),
pickupId: z.number().optional(),
}),
payment: z.object({
method: z.enum(['online', 'cash', 'invoice']),
}),
});
Checkout state is managed with Zustand: steps, current step, data per step, calculation result. When moving between steps, data is preserved and the user can go back.
We use React Hook Form for validation, Zustand for state, and React Query for queries.
| Technology |
Usage |
| React Hook Form + Zod |
Form validation with minimal re-renders |
| Zustand |
Lightweight state manager for steps and UI states |
| React Query |
Caching Bitrix API requests, automatic retry on failures |
Integration with maps for courier delivery
Yandex Maps or DaData for address autocomplete is a standard task for React checkout.
// Hook for address autocomplete via DaData
function useAddressSuggest(query: string) {
return useQuery({
queryKey: ['address-suggest', query],
queryFn: () => fetchDaDataSuggestions(query),
enabled: query.length > 3,
staleTime: 60_000,
});
}
When an address is selected via DaData, structured data (city, street, postal code) is passed to Bitrix as separate fields — this simplifies further order processing and handoff to delivery services.
Case study: checkout for a furniture retailer
Our client: an online furniture store with 2000+ SKUs. Specifics: items with different production times (3 to 40 days), option to choose a delivery date, mandatory measuring for some items, B2B checkout with legal entity details. The standard sale.order.ajax did not support delivery date selection, conditional display of the measuring block, or company details in a single flow.
Implementation:
-
Step 1 — Contact. A form with phone and name. Phone validated via libphonenumber-js, SMS verification optional.
-
Step 2 — Delivery. Dynamic display: if the order contains items requiring measuring, a "Schedule measuring" block appears with a datepicker. Available dates are loaded from the server (from Bitrix CRM, occupied slots are blocked). Delivery date selection accounts for production time — the minimum date is calculated server-side as
max(PRODUCTION_DAYS) in the cart.
-
Step 3 — Payment. A toggle "Individual / Legal entity". When legal entity is selected, a block with company details expands (INN → autocomplete via DaData → pulls KPP, name, address). A non-cash invoice for B2B is automatically generated after order creation via
\Bitrix\Sale\PaySystem\Manager.
-
Order creation. A final POST sends all data to the server. Bitrix creates the order, attaches custom properties (delivery date, client type, company details), sends notifications. React receives the order ID and redirects to the "Thank you" page.
| Feature |
Standard Bitrix |
React checkout |
| Delivery date selection |
Impossible |
Datepicker with busy slots |
| B2B details |
Separate form |
Inline, same flow |
| Real-time validation |
Only on submit |
Instant, on blur |
| Total update on delivery change |
Block reload (>1 s) |
No reload (<200 ms) |
Conversion increased from 62% to 79% within the first 6 weeks after launch.
Server-side order creation
public function createOrderAction(array $data): array
{
$order = \Bitrix\Sale\Order::create(SITE_ID, $this->getCurrentUserId());
$basket = \Bitrix\Sale\Basket::loadSiteBasket(SITE_ID);
$order->setBasket($basket);
// Contact
$order->setField('USER_DESCRIPTION', $data['comment'] ?? '');
// Delivery
$shipmentCollection = $order->getShipmentCollection();
$shipment = $shipmentCollection->createItem(
\Bitrix\Sale\Delivery\Services\Manager::getById($data['delivery_id'])
);
$shipment->setField('DELIVERY_ID', $data['delivery_id']);
// Payment
$paymentCollection = $order->getPaymentCollection();
$payment = $paymentCollection->createItem(
\Bitrix\Sale\PaySystem\Manager::getObjectById($data['payment_id'])
);
$payment->setField('PAY_SYSTEM_ID', $data['payment_id']);
$payment->setField('SUM', $order->getPrice());
// Order properties (address, phone, INN, etc.)
$propertyCollection = $order->getPropertyCollection();
foreach ($data['properties'] as $code => $value) {
$prop = $propertyCollection->getItemByOrderPropertyCode($code);
if ($prop) {
$prop->setValue($value);
}
}
$result = $order->save();
if (!$result->isSuccess()) {
throw new \Exception(implode(', ', $result->getErrorMessages()));
}
return ['order_id' => $order->getId()];
}
Error handling and edge cases
Insufficient stock during checkout is handled at the final save step. React shows a modal with a list of unavailable items and offers to remove them or save the order without them.
Connection loss during checkout — React Query with retry: 3 and notification to the user. Form data is saved in sessionStorage and restored on reload.
What's included in the work
- Designing checkout steps, conditional logic, validation
- Developing API controllers: order calculation, creation, fetching delivery services and pickup points
- Creating the React app: form, state manager, map/DaData integration
- Binding custom order properties, configuring payment systems
- Testing edge cases: empty cart, insufficient stock, session timeout
- API and code documentation, training your team
Project timelines and process
Our process: data collection → audit/analysis → design → estimate → development → testing → launch. Timelines range from two weeks for a basic checkout to two months for a complex multi-step solution with B2B features and map integration. The exact timeline is determined after analysis of your requirements.
Ready to speed up your checkout? Order React checkout development — contact us. Get a consultation for your project — we'll tell you exactly which steps are needed.
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
- Audit existing code — find bottlenecks: redundant requests, outdated templates, suboptimal caches.
- Design API layer — determine which endpoints are needed, design aggregators or GraphQL.
- Develop design system — create components (buttons, forms, cards) based on mockups or UX recommendations.
- Integrate with Bitrix via chosen approach (SPA, SSR, or Headless) — set up rendering and routing.
- 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.