SPA Development with Vue.js for 1C-Bitrix
An SPA (Single Page Application) built with Vue on top of Bitrix means the entire client-side interface is managed by Vue Router, while Bitrix acts as the API server and generates a single HTML page with a mount point. The primary challenge is SEO and initial load: without SSR, search engine bots see an empty div.
Architecture: Bitrix as a BFF
Bitrix becomes the Backend for Frontend. The structure:
-
Site template (
header.php,footer.php) — minimal HTML, meta tags (rendered server-side for SEO pages), Vue bundle inclusion - Routing — on the Bitrix side, a single rule is configured: all requests to the SPA section return one template. Other site sections operate normally
-
API — custom controllers via
\Bitrix\Main\Engine\Controlleror therestmodule's REST API
Example nginx routing for the /app/ SPA section:
location /app/ {
try_files $uri /app/index.php;
}
Vue Router + Browser History
Vue Router in createWebHistory() mode — URLs look like /app/catalog/123 without hashes. For correct operation, Bitrix must serve a single PHP template for all URLs within the section:
// In index.php for the /app/ section
define('STOP_STATISTICS', true); // Don't log every client-side route to b_stat
$APPLICATION->SetTitle(''); // SPA manages the title itself
require($_SERVER['DOCUMENT_ROOT'] . '/bitrix/header.php');
echo '<div id="spa-app"></div>';
require($_SERVER['DOCUMENT_ROOT'] . '/bitrix/footer.php');
Authentication in the SPA
The user is authenticated in Bitrix through a standard PHP session. The Vue application requests the current user on initialization:
// api/user.js
export async function getCurrentUser() {
const response = await fetch('/bitrix/services/main/ajax.php?action=user.get');
return response.json();
}
For token-based authentication (JWT) — a custom module is required; Bitrix does not natively support JWT.
State Management (Pinia)
For an SPA with authentication, a cart, and a catalog — three stores:
// stores/auth.js
export const useAuthStore = defineStore('auth', {
state: () => ({ user: null, isLoading: false }),
actions: {
async fetchUser() { /* GET /api/user */ },
async logout() { /* POST /api/logout */ }
}
});
The Bitrix session is synchronized with Pinia via API requests, not via localStorage — this is important for security.
SEO for SPAs
An SPA without SSR is a problem for Yandex and Google. Options:
-
Prerendering (via
vite-plugin-prerenderor Puppeteer) — for static catalog pages -
SSR (
nuxtorvite-ssr) — if Bitrix sits behind a Node.js proxy (complex infrastructure) - Hybrid: SEO-critical pages are rendered server-side by Bitrix, the SPA is used only for authenticated users (account, cart)
For most projects, the hybrid approach is optimal: public pages (catalog, product page) — Bitrix; personal account and checkout — SPA.
Real-World Case
A B2B dealer portal: after login, the user enters an SPA with a catalog, cart, order history, and sub-account management. The public part of the site is standard Bitrix with SEO. Separation: /dealer/* — SPA, everything else — standard Bitrix.
Tech stack: Vue 3 + Pinia + Vue Router, Vite for bundling, bundle in /local/templates/main/dealer-app/. API — custom controllers on \Bitrix\Main\Engine\Controller with authentication via the standard Bitrix session. Development time — 25 business days.
Delivery Timelines
| SPA Scale | Timeline |
|---|---|
| Simple account (order history, profile) | 8 to 15 business days |
| Dealer portal (catalog, cart, accounts) | 20 to 35 business days |
| Full SPA replacing the public-facing site | from 40 business days |







