Your Bitrix feedback form opens in the standard BX.PopupWindow? When you try to add multi-step logic or asynchronous photo uploads, you quickly hit the limits of built-in rendering — the DOM is recreated on each open, events are not inherited, debugging becomes a quest. Developers often try to extend BX.PopupWindow via jQuery DOM manipulations, but this leads to fragile code and conflicts with component caching.
We are experts in pop-up development for Bitrix, having completed over 35 custom modal windows projects. Our extensive experience and proven methodology guarantee reliable, high-performance modal windows that meet your business needs. We prefer a different approach: an isolated Vue application that mounts at the call point and manages all states reactively. Our accumulated experience — over 30 projects integrating Vue.js into Bitrix — from simple forms to complex multi-step carts. This approach reduces development time by 40% and simplifies maintenance. Our approach ensures custom modal windows with Vue 3 components, reactive forms, and pop-up development that scales. We have reduced costs by up to 50% compared to traditional jQuery methods.
How the architecture of a Vue.js modal window in Bitrix works
Integration is built through a mount point in the component template. Instead of global application initialization, we mount an isolated instance on a specific element:
// local/templates/main/components/project/modal-form/template.php <div id="vue-modal-root" data-form-id="<?= $arResult['FORM_ID'] ?>" data-endpoint="<?= $arResult['AJAX_URL'] ?>"> </div> <script> BX.ready(function() { const { createApp } = Vue; const app = createApp(ModalApp, { formId: document.getElementById('vue-modal-root').dataset.formId, endpoint: document.getElementById('vue-modal-root').dataset.ajaxEndpoint }); app.mount('#vue-modal-root'); }); </script> Data from Bitrix is passed via data-* attributes or through the global window.BX_STATE object formed in result_modifier.php. Direct PHP interpolation in JS code is forbidden — it breaks component caching. The structure of the Vue modal component includes:
- ModalWrapper.vue — wrapper with overlay, manages z-index, scroll blocking, Escape key handling
- ModalContent.vue — slot for content, entry/exit animations via Transition
- ModalTrigger.vue — trigger button, emits open event via provide/inject or Pinia store
To manage the state of multiple modals on one page, we use useModalStore on Pinia — a stack of open modals so that closing the top one does not close the one below.
Why Vue.js is better than jQuery for modal windows in Bitrix?
Comparison of key parameters:
| Parameter | BX.PopupWindow + jQuery | Vue.js |
|---|---|---|
| Reactive state | No (manual via DOM) | Yes (data, computed) |
| Form validation | External library | VeeValidate or computed |
| Animations | CSS classes manually | Transition |
| Component caching | Works poorly | Works normally (isolation) |
| Development time for a form | 3-5 days | 1-2 days |
We have gone from jQuery to Vue on dozens of projects — the difference in performance and maintenance time is obvious.
What typical scenarios do we solve?
Feedback form with validation. Fields are validated reactively via VeeValidate. After submission, an AJAX request is sent to the Bitrix handler through bitrix:form.result.new. The response is processed in the component — no page reload.
Modal cart. When a user adds a product, the modal shows the current cart state. Data is fetched via Bitrix REST API: /api/v1/sale/basket/ or a custom AJAX controller on Bitrix\Main\Engine\Controller. The quantity updates reactively.
Image gallery / lightbox. The Vue component receives an image array via a data-images attribute or preloaded JSON. We use swipe gestures via the vue-use/useSwipe library and keyboard navigation via onKeydown.
Multi-step forms. Steps are separate Vue components, state is stored in the parent setup(). A progress bar reacts to the current step. All data accumulates and is sent in one request on the last step.
Case from our practice: modal application form on a promo page
A promo site for one client: several CTA buttons on the page, each opens the same form but with a different utm_source and a pre-filled field "How did you hear about us?". The standard solution via BX.PopupWindow would require JS DOM manipulations on each open. We did it differently: one mount point in

