Typical situation: a designer created a catalog with animated filters, instant cart updates, and page transitions without reload. The frontend developer looks at the bitrix:catalog.section and bitrix:sale.order.ajax templates and realizes that fitting this into the standard Bitrix component model is impossible without crutches. That's where headless approach comes in: Bitrix remains the backend, and the entire interface lives on Vue.js. This isn't a trendy stack for the sake of fashion. Headless is justified when standard Bitrix templates cannot deliver the required UX, when the frontend team works independently from backend developers, or when one API serves the website, mobile app, and kiosks in offline locations. We have been using this approach for over 5 years and have implemented more than 20 projects — from small catalogs to B2B portals with tens of thousands of products. Experience shows that with proper architecture, headless delivers modern UX and high performance, with a 12-month warranty on the work.
Why headless on Bitrix is a justified solution?
For projects where the standard Bitrix interface does not meet requirements for animation, transition speed, or the need to work on mobile devices with JavaScript disabled (SSR). Headless also allows separating teams: frontend developers work with Vue, backend with Bitrix, and the contract is an API specification. Additionally, this approach naturally paves the way for multi-platform — one API serves the website, app, and kiosks. The ROI for headless is about 6–12 months, thanks to up to 30% reduction in frontend maintenance costs.
Architecture: how layers are separated
In a classic Bitrix store, a PHP component fetches data, passes an array to template.php, where CSS and JS are also included. In the headless scheme, everything is different:
- Bitrix operates as an API server. Catalog, prices, stock, cart, checkout, authorization — all via REST API (module
rest) or custom controllers based on\Bitrix\Main\Engine\Controller. - Vue.js / Nuxt.js — a separate application. Renders the interface, manages routing, state, forms.
- Nginx proxies:
/api/*goes to Bitrix, the rest goes to Vue static or Node.js (with SSR).
Frontend and backend deployments are independent. The frontend developer pushes to their repository, CI builds the bundle, deploys to CDN or Node server. The backend updates Bitrix separately. The contract between them is the API specification.
| Parameter | Classic Bitrix | Headless (Vue + Bitrix) |
|---|---|---|
| Rendering | Server-side (PHP) | Client-side + CSR/SSR |
| Caching | Composite cache | ISR, CDN, Service Worker |
| Development | Tied to templates | Independent |
| Indexing | Out of the box | Requires SSR |
| UI Flexibility | Limited by components | Maximum |
Bitrix REST API: what works and what needs to be supplemented
The rest module provides methods for main store entities:
-
catalog.product.list— products with filtering by properties, sections, prices -
catalog.product.get— detailed card -
catalog.product.offer.list— trade offers (SKUs) -
catalog.section.list— category tree -
catalog.price.list— prices by type -
sale.basket.addItem,sale.basket.updateItem,sale.basket.deleteItem,sale.basket.getItems -
sale.order.add,sale.order.get,sale.order.list -
sale.shipment.getDeliveryServices,sale.paySystem.getList
On paper, everything is covered. In practice, nuances appear. catalog.product.list does not return arbitrary infoblock properties. You need to additionally request via catalog.product.getFieldsByFilter or write your own endpoint. Faceted filtering — counting the number of products for each filter value, as in the standard smart_filter — is absent in the REST API. Calculating delivery cost based on cart contents is another method missing out of the box.
The solution is custom REST methods. They are registered via \CRestServer::onRestServiceBuildDescription() or via \Bitrix\Main\Engine\Controller with the @restMethod annotation. On the Bitrix side, the custom controller performs the query and returns JSON:
-
/api/catalog/filter— products + facets (counts per filter value) -
/api/cart/calculate— cart recalculation considering cart rules, discounts, and promo codes -
/api/checkout/submit— checkout in one request
Facet index is a separate story. Bitrix stores precomputed facets in the b_catalog_smart_filter table. In a headless approach, you either use this table directly via ORM or build facets on the fly. The first option is faster but ties you to Bitrix's internal structure. The second is slower on large catalogs (50,000+ products) but predictable. According to Wikipedia, REST is an architectural style for interaction between components of a distributed application over a network.
Technical details for senior developers
For optimizing large catalogs, it is recommended to use HL blocks for storing additional properties and tagged caching. For example, when updating a product price via an agent, you can invalidate only the tagged cache without affecting other pages. Agents and events (`OnAdminListDisplay`, `OnSaleOrderSaved`) help synchronize data between Bitrix and external services like CDEK or YooKassa.Component architecture of the Vue application
The frontend structure for an online store:
src/
├── pages/
│ ├── CatalogPage.vue # product list with filters
│ ├── ProductPage.vue # product card
│ ├── CartPage.vue # cart
│ ├── CheckoutPage.vue # checkout
│ └── AccountPage.vue # personal account
├── components/
│ ├── catalog/
│ │ ├── ProductCard.vue
│ │ ├── FilterPanel.vue
│ │ └── FacetCounter.vue
│ ├── cart/
│ │ ├── CartItem.vue
│ │ └── CartSummary.vue
│ └── ui/ # reusable elements
├── stores/
│ ├── catalogStore.ts # Pinia: products, filters, pagination
│ ├── cartStore.ts # cart, API synchronization
│ ├── userStore.ts # authorization, token
│ └── checkoutStore.ts # checkout
├── api/
│ ├── catalog.ts # wrappers over catalog API
│ ├── cart.ts
│ └── auth.ts
└── composables/
├── useProductFilter.ts # filtering logic
└── useInfiniteScroll.ts # infinite scroll
Pinia manages state. cartStore is the most non-trivial: when adding a product, you need to instantly update the UI (optimistic update), send a request to the API, get a response with the actual price (Bitrix may have applied a discount or deducted stock) and synchronize local state with the server. For unauthorized users, the cart lives in localStorage and migrates to the server after login. Vue Router with lazy loading: each page is a separate chunk. Transitions between categories do not reload the app, and filters are written to URL query parameters for shareable links.
How to set up SSR for indexing?
A Vue SPA renders on the client. A search bot sees an empty <div id="app"></div>. For an online store where product cards and categories must be indexed, this is a death sentence.
Nuxt.js with SSR is the main option. A Node.js server renders Vue components into HTML; data from the Bitrix API is fetched via useFetch() or useAsyncData(). The client receives ready HTML; after hydration, the app works as an SPA. Using SSR reduces time to first paint by 40–60%.
Nuxt.js with ISR (Incremental Static Regeneration) is a hybrid approach. Catalog pages are cached and updated based on TTL or a webhook from Bitrix when a product changes. Nuxt 3 supports routeRules with swr (stale-while-revalidate):
// nuxt.config.ts
routeRules: {
'/catalog/**': { swr: 3600 }, // cache for an hour
'/product/**': { swr: 600 }, // cache for 10 minutes
'/cart': { ssr: false }, // cart — client only
'/checkout': { ssr: false },
}
For catalogs with 50,000+ products, SSR is preferable to full static generation — nuxt generate for such a volume would take hours. Meta tags are a separate task. In Bitrix, SEO templates are configured in infoblock properties (templates like {=this.Name} buy in Minsk). In a headless approach, these templates need to be served via API and applied in Nuxt using useHead() or useSeoMeta().
Authorization: two approaches
OAuth 2.0 via the rest module: the frontend redirects to /oauth/authorize/, the user logs in on the Bitrix side, receives a code, exchanges it for an access_token. Standard flow, but UX suffers — redirect to another domain.
Custom JWT endpoint: /api/auth/login accepts login/password, Bitrix verifies via CUser::Login(), creates a session and returns a JWT. The frontend stores the token in an httpOnly cookie (not in localStorage — otherwise XSS vulnerability). A refresh token extends the session without re-entering password. Simpler to implement, better UX.
What is lost with headless?
- Visual editor — does not work. Content is managed through the Bitrix admin panel; the frontend fetches data via API.
- Composite cache — not applicable. Caching on the Nuxt side (ISR) or CDN.
- Standard components —
bitrix:catalog.section,bitrix:sale.order.ajaxare not used. All display logic is on Vue. - Exchange with 1C — works unchanged, as it's server-side.
What stages does development include?
- Analysis — we study the current Bitrix architecture, UX and performance requirements, determine the list of custom API methods.
- Design — we develop an API specification (OpenAPI), a UI prototype, and agree with you.
- Development — we write custom REST methods, the Vue application, configure SSR, integrate with external services.
- Testing — load testing (with k6 or Artillery), SEO meta tag verification, cross-browser compatibility.
- Deployment — CI/CD setup, production deployment, monitoring.
What is included in the work?
- Complete API specification (OpenAPI) for all custom methods.
- Source code of the Vue application with comments.
- Deployment and CI/CD setup instructions.
- Automated tests for critical scenarios.
- Training your team to work with the headless architecture.
- 1 month of technical support after launch.
Timelines by project scale:
| Scope | What's included | Timeline |
|---|---|---|
| MVP catalog | Listing, product card, filters, SSR | 1–2 weeks |
| Store without account | + cart, checkout, payment | 3–4 weeks |
| Full-fledged store | + account, order history, favorites, compare | 5–8 weeks |
| B2B portal | + price types by group, personal catalogs, quick order | 8–12 weeks |
Headless on Bitrix is a trade-off. Modern frontend and flexibility in exchange for losing part of the ecosystem and increased support costs. The approach is justified for projects with high interface requirements, a dedicated frontend team, and plans for multi-platform. Switching to headless can reduce frontend maintenance costs by up to 30% due to team independence. To evaluate your project, contact us and we will prepare a commercial proposal with exact timelines and cost. Request a consultation on headless development for your online store.







