Developing an online store frontend using Vue.js for 1C-Bitrix

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.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1175
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • 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
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Frontend Development for E-commerce on Vue.js for 1C-Bitrix

A typical scenario: a designer has created a catalog with animated filters, instant cart updates, and page transitions without reloads. A frontend developer looks at the bitrix:catalog.section and bitrix:sale.order.ajax templates and realizes that fitting this into Bitrix's standard component model is impossible without workarounds. This is where the headless approach comes in: Bitrix remains the backend, while the entire interface lives on Vue.js.

This isn't "trendy tech for trend's sake." Headless is justified when standard Bitrix templates don't allow you to implement the required UX, when the frontend team works independently from backend developers, or when a single API serves the website, mobile app, and offline kiosks.

Architecture: How the Layers Are Separated

In a classic Bitrix shop, a PHP component performs the query, passes an array to template.php, where CSS and JS are also included. In a headless scheme, everything is different:

  • Bitrix works as an API server. Catalog, prices, stock, cart, checkout, authentication — all through REST API (the rest module) 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, everything else goes to static Vue or Node.js (for SSR).

Frontend and backend deployment are independent. The frontend developer pushes to their own repository, CI builds the bundle and deploys to CDN or a Node.js server. The backend developer updates Bitrix separately. The contract between them is the API specification.

Bitrix REST API: What Works and What Needs Custom Development

The rest module provides methods for the main store entities.

Catalog:

  • catalog.product.list — products with filtering by properties, section, price
  • catalog.product.get — product card details
  • catalog.product.offer.list — trade offers (SKUs)
  • catalog.section.list — category tree
  • catalog.price.list — prices by type

Cart:

  • sale.basket.addItem, sale.basket.updateItem, sale.basket.deleteItem, sale.basket.getItems

Order:

  • sale.order.add, sale.order.get, sale.order.list
  • sale.shipment.getDeliveryServices, sale.paySystem.getList

On paper, everything is covered. In practice, nuances begin.

catalog.product.list doesn't return arbitrary infoblock properties. You need to request additionally through catalog.product.getFieldsByFilter or write your own endpoint. Faceted filtering — counting the number of products for each filter value, like the standard smart_filter — is absent from the REST API. Calculating shipping cost based on cart contents — another method that's not provided 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, a custom controller executes the query and returns JSON:

  • /api/catalog/filter — products + facets (count by filter values)
  • /api/cart/calculate — recalculate cart considering rules, discounts, and promo codes
  • /api/checkout/submit — checkout in a single request

The faceted index is a separate story. Bitrix stores pre-calculated 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 more predictable.

Component Architecture of the Vue Application

Structure of a frontend for an e-commerce store:

src/
├── pages/
│   ├── CatalogPage.vue        # product list with filters
│   ├── ProductPage.vue         # product card
│   ├── CartPage.vue            # shopping cart
│   ├── CheckoutPage.vue        # checkout
│   └── AccountPage.vue         # user 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 sync
│   ├── userStore.ts            # auth, token
│   └── checkoutStore.ts        # checkout
├── api/
│   ├── catalog.ts              # catalog API wrappers
│   ├── cart.ts
│   └── auth.ts
└── composables/
    ├── useProductFilter.ts     # filtering logic
    └── useInfiniteScroll.ts    # infinite scroll

Pinia manages state. cartStore is the most non-trivial: when a product is added, you need to instantly update the UI (optimistic update), send a request to the API, get a response with actual prices (Bitrix may have applied a discount or depleted stock), and sync 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 don't reload the application; filters are written to URL query parameters for sharing links.

SSR: You Can't Do Without It in E-commerce

Vue SPA renders on the client. A search bot sees an empty <div id="app"></div>. For an e-commerce store where product cards and categories should be indexed, this is a dead end.

Nuxt.js with SSR — the main option. A Node.js server renders Vue components to HTML; data from Bitrix API is fetched via useFetch() or useAsyncData(). The client receives ready HTML; after hydration, the app works as an SPA.

Nuxt.js with ISR (Incremental Static Regeneration) — a hybrid. Catalog pages are cached and updated by TTL or 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 that volume would take hours.

Meta tags — 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 returned via API and applied in Nuxt via useHead() or useSeoMeta().

Authentication

Two approaches:

OAuth 2.0 via the rest module: the frontend redirects to /oauth/authorize/, the user logs in on Bitrix, gets a code, exchanges it for access_token. A standard flow, but UX suffers — redirect to another domain.

Custom JWT endpoint: /api/auth/login accepts username/password, Bitrix validates via CUser::Login(), creates a session and returns 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 a password. Simpler to implement, better UX.

What Is Lost in Headless

  • Visual editor — doesn't work. Content is managed via Bitrix admin panel, frontend fetches data via API.
  • Composite cache — not applicable. Caching on Nuxt (ISR) or CDN side.
  • Standard componentsbitrix:catalog.section, bitrix:sale.order.ajax are not used. All display logic is on Vue.
  • 1C exchange — works unchanged; it's server-side.

Timeline by Project Scale

Scale What's Included Timeline
MVP catalog Listing, product card, filters, SSR 1–2 weeks
Shop without account + cart, checkout, payment 3–4 weeks
Full-featured store + account, order history, wishlist, comparison 5–8 weeks
B2B portal + price types by groups, personal catalogs, quick order 8–12 weeks

Headless on Bitrix is a compromise. A modern frontend and flexibility in exchange for losing parts of the ecosystem and increased support costs. The approach is justified for projects with high interface demands, a dedicated frontend team, and plans for multiplatform support.