React Development for 1C-Bitrix
CIBlockElement::GetList Returns JSON, React Renders It — Sounds Simple Until You Start
The main fork at the start: how exactly React will fetch data from Bitrix. Everything depends on this — how much code to write, how to deploy, what about SEO, and how to maintain it all later. Over ten years we've tried every option, and each has its own pitfalls.
Architectural Approaches
SPA on React + Bitrix REST API (BX.rest)
The most flexible option. The React application lives separately and calls /rest/ or custom endpoints via CRestServer. Maximum control, but also maximum effort.
- Client-side routing via React Router — transitions without page reloads, but on F5 you need a catch-all on Nginx (
try_files $uri /index.html) - Optimistic updates: the cart updates instantly,
sale.basket.updatefires in the background. If it fails — we roll back the state and show a toast - Caching via React Query with
staleTime— the catalog doesn't hit the server on every back navigation - The frontend deploys to a CDN independently from Bitrix — update a button without touching the backend
SSR with Hydration — When Yandex Can't See the SPA
Yandex has learned to render JS, but poorly. Googlebot is better, but still not 100%. Server-side rendering of React components via Node.js solves the problem radically: the bot gets ready HTML, the user gets an interactive application after hydration.
- FCP drops below one second on decent hosting
-
og:title,og:imagework for social sharing — without SSR, WhatsApp will show an empty preview - Complexity: you need a Node.js process alongside Apache/Nginx serving Bitrix. Two runtimes, two deployments, two sets of logs
- Bitrix cache (
CPHPCache, Composite) can be used to warm up data that then feeds into SSR
Headless Bitrix — Admin Panel for Content Managers, React for Visitors
The content manager goes to /bitrix/admin/ and edits information blocks. The visitor sees a React application that fetches data via API. Classic headless.
- One backend serves the website, mobile app, and Telegram bot — through the same endpoints
- Scaling: React bundle on CloudFront/CDN, Bitrix on a single server. Even at 50k unique visitors, the frontend doesn't load the backend directly
- Pitfall: the standard Bitrix visual editor (
BXEditor) stops working for visitors. Content managers will have to work only through the admin panel, without inline editing
Inertia.js — A Bridge Without REST API
Inertia passes data from the PHP controller directly into the React component as props. No need to write a separate API, no response serializer, no Swagger. Server-side routing, but client-side rendering.
- Forms:
useForm()from Inertia instead of fetch + manual error handling. Server-side validation arrives automatically - Authorization via standard Bitrix sessions —
$USER->IsAuthorized()works as usual - Downside: tight coupling. The frontend doesn't work without the backend, and you can't connect a separate mobile app
The Stack We Actually Use
| Technology | Why Specifically |
|---|---|
| React 18+ | Suspense, useTransition — the UI doesn't block during heavy catalog updates |
| TypeScript | Typing Bitrix API responses — IBlockElement, BasketItem, Order. Without this, refactoring turns into Russian roulette |
| Vite | HMR in 50ms vs 3-5 seconds on webpack. On a project with 200 components, the difference is noticeable |
| React Query | useQuery(['catalog', sectionId]) — automatic caching, revalidation, retry on 503 from an overloaded Bitrix |
| React Hook Form + Zod | Checkout: 15-20 fields, conditional validation (legal entity — one set of fields, individual — another). RHF doesn't re-render the form on every keystroke |
| Tailwind CSS | Utility classes. No fighting with the cascade from Bitrix's template_styles.css |
| Radix UI / Shadcn | Accessible primitives. Modals, dropdowns, tabs — with ARIA out of the box |
Component Approach: Where the Pitfalls Are
Project Component Library
Every project starts with a design system. Not out of love for order, but because without one, by the third month three developers will have written three different button components.
- Typography, colors, spacing — via CSS variables and Tailwind config
- Forms: inputs with masks (phone, tax ID), selects with search, file uploads with preview and MIME validation
- Product card — a story of its own. Price factoring in discounts from
CCatalogProduct::GetOptimalPrice(), "Bestseller"/"New" labels from information block properties, "Add to cart" button with loading/success/error states - Tables with virtualization (react-window) for price lists with 5,000+ rows
Typing: Not a Formality, But a Lifesaver
We type everything that comes from Bitrix. Because the REST API returns string where you expect number, "Y"/"N" instead of boolean, and null instead of an empty array.
// The actual response type from 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 too
}
A Zod schema at the input parses and transforms — components receive proper types.
Performance: Where Bitrix + React Slows Down
The Main Pain Point — Number of Requests
A standard catalog page: product list, filters, breadcrumbs, banner, SEO text. If you make a separate REST API request for each block — you get 6-8 requests at 200-500ms each. Total: 2-3 seconds.
The solution — aggregating endpoints. A single ajax.php or custom controller on \Bitrix\Main\Engine\Controller gathers all data for the page in one request. React Query caches the response; a repeat visit comes from cache.
Virtualization — Not Optional, But Essential
A catalog with a faceted filter can return 500 products per page (it happens when the SEO specialist "optimizes" pagination). React-window or react-virtuoso renders only the visible 20-30 cards. The DOM doesn't bloat, scrolling stays smooth.
Core Web Vitals — Specific Numbers
-
LCP < 2.5s — lazy loading images via
loading="lazy", critical CSS inline, preloading the LCP image via<link rel="preload"> -
INP (replaced FID) < 200ms —
useTransitionfor heavy filtering,useDeferredValuefor the search input - CLS < 0.1 — fixed dimensions for skeletons and images. Skeleton placeholders instead of spinners
Integration with Bitrix API: The Reality
Standard REST — Covers 30% of Tasks
Out of the box via /rest/, you get: information blocks (iblock.element.get), cart (sale.basket.*), orders (sale.order.*), users (user.*). For a simple catalog — it's enough.
The Other 70% — Custom Controllers
\Bitrix\Main\Engine\Controller — the standard way to create your own endpoints in D7. Write a controller, register via registerAction, and get an 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 layer (webonyx/graphql-php) on top of D7 ORM — the frontend requests exactly the fields it needs. Traffic savings on mobile
Typical Projects
- An online store with 30,000 SKUs with a faceted filter via
\Bitrix\Iblock\PropertyIndex\Facet— SPA, React Query, catalog virtualization - B2B portal: personalized prices from
CCatalogGroup, reconciliation reports 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), shared backend, data separation via
CUser::GetUserGroup()
Timelines
| Project Type | Timeline |
|---|---|
| Landing page on React + Bitrix | 2-4 weeks |
| SPA online store | 8-16 weeks |
| Corporate portal | 10-20 weeks |
| Frontend migration to React (phased) | 6-12 weeks |
Exact figures — after reviewing the requirements. Estimation is phased, with a fixed budget per sprint.
Why React, Not Vue or Bitrix Templates
- Ecosystem. For any UI task, there's a ready-made library: tables, charts, drag-and-drop, virtualization
- Talent. Finding a React developer is 3x easier than finding a Bitrix template developer who knows D7 and
template.php - React Native. Components are reused in the mobile app — not one-to-one, but business logic and types are shared
- Gradual adoption. You can start with one section (
/catalog/) on React, leaving the rest on Bitrix templates.component_epilog.phploads the React bundle, data is passed viawindow.__INITIAL_DATA__
1C-Bitrix + React is not a theoretical architecture, but a proven combination that already powers catalogs with tens of thousands of SKUs and B2B portals with complex business logic.







