Configuring Vuex/Pinia for State Management in 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
    1173
  • 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
    745
  • 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

Vuex/Pinia Setup for State Management in 1C-Bitrix

When two or more Vue components on a Bitrix page need to share common data — cart state, current user, catalog filters — a centralized store is required. Without it, each component makes a separate AJAX request to the same endpoint, data falls out of sync, and adding a product to the cart in one component doesn't update the counter in the header.

Pinia vs Vuex in 2024+ Projects

For new Vue 3 projects — Pinia. Vuex 4 is still maintained but has officially been superseded by Pinia. Key differences in the Bitrix context:

Pinia requires no mutations — state is changed directly in actions. Excellent TypeScript support. DevTools work out of the box. Less boilerplate.

Vuex is justified if the project is already on Vue 2 with Vuex 3, or if the team knows Vuex well and migration is not practical.

Store Initialization in a Bitrix Context

When multiple Vue applications are mounted on different page elements (the cart in the header and the product block at the bottom are separate createApp() calls), they don't automatically share a store. The solution — a singleton via window:

// store/index.js
import { createPinia } from 'pinia';

const pinia = window.__pinia || (window.__pinia = createPinia());
export default pinia;

// In each app.js
import pinia from './store/index.js';
const app = createApp(Component);
app.use(pinia);
app.mount('#mount-point');

Both Vue applications use the same Pinia instance — cart state is synchronized.

Cart Store for Bitrix

// stores/cart.js
import { defineStore } from 'pinia';

export const useCartStore = defineStore('cart', {
    state: () => ({
        items: [],
        loading: false,
        initialized: false,
    }),
    getters: {
        totalCount: (state) => state.items.reduce((sum, i) => sum + i.quantity, 0),
        totalPrice: (state) => state.items.reduce((sum, i) => sum + i.price * i.quantity, 0),
    },
    actions: {
        async init() {
            if (this.initialized) return;
            this.loading = true;
            const res = await fetch('/api/v1/cart/');
            this.items = await res.json();
            this.initialized = true;
            this.loading = false;
        },
        async addItem(productId, quantity = 1) {
            const res = await fetch('/api/v1/cart/add/', {
                method: 'POST',
                headers: { 'X-Bitrix-Csrf-Token': window.BX_STATE.csrf },
                body: JSON.stringify({ productId, quantity }),
            });
            const updated = await res.json();
            this.items = updated.items;
        },
    },
});

A custom Bitrix-side controller calls \Bitrix\Sale\Basket::loadItemsForFUser() and returns JSON. The CSRF token (bitrix_sessid()) is passed from PHP into window.BX_STATE.csrf.

Authorization and User Store

export const useUserStore = defineStore('user', {
    state: () => ({
        id: window.BX_STATE?.userId || null,
        groups: window.BX_STATE?.userGroups || [],
        priceTypeId: window.BX_STATE?.priceTypeId || 1,
    }),
    getters: {
        isAuthorized: (state) => !!state.id,
        isWholesale: (state) => state.groups.includes(WHOLESALE_GROUP_ID),
    },
});

User data is initialized from window.BX_STATE, which is assembled in PHP once when the page loads. No AJAX requests needed to obtain basic user information.

State Persistence

For data that needs to persist between pages (e.g., selected catalog filters), use pinia-plugin-persistedstate:

pinia.use(piniaPluginPersistedstate);

export const useFiltersStore = defineStore('filters', {
    state: () => ({ selectedBrands: [], priceRange: [0, 100000] }),
    persist: { storage: sessionStorage },
});

sessionStorage is preferable to localStorage for filters — data resets when the tab is closed, preventing stale values from accumulating.

DevTools and Debugging

Vue DevTools (browser extension) shows the state of all Pinia stores in real time — a key tool during debugging. On production, Pinia DevTools are automatically disabled in Vite's production build. Mutation logging via Pinia $subscribe — for debugging complex update scenarios.

Typical setup timeline: for a project without an existing store, with 2–3 components that need shared data (cart, authorization, notifications) — 1–2 business days, including persistence setup and integration with Bitrix PHP controllers.