Custom Modal Windows for 1C-Bitrix with Vue.js: Expert Development Guide

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 exten

Our competencies:

Frequently Asked Questions

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 , Vue component LeadModal accepts props via eventBus. Buttons dispatch a custom event:

document.querySelectorAll('[data-modal-trigger]').forEach(btn => { btn.addEventListener('click', () => { window.dispatchEvent(new CustomEvent('open-lead-modal', { detail: { source: btn.dataset.source, productId: btn.dataset.productId } })); }); }); 

The Vue component listens for the event via onMounted:

window.addEventListener('open-lead-modal', (e) => { formData.source = e.detail.source; formData.productId = e.detail.productId; isOpen.value = true; }); 

The form sends data to the Bitrix CRM module via REST API (crm.lead.add), the status is written to b_user_field. Development took 3 days instead of a week for the jQuery version — more than 50% savings. This project cost approximately $2,500 and saved 50% development time compared to jQuery. Over 50 forms similar to this have been handled by our components. "Previously the modal opened with a delay, now it's instant, and the code is much cleaner" — client feedback.

How we ensure Vue modal performance?

Modal windows are rendered only when opened — we use v-if, not v-show, to keep the DOM lean. For heavy components (video player, map) we use defineAsyncComponent:

const HeavyMap = defineAsyncComponent(() => import('./HeavyMap.vue')); 

The chunk loads only when the user opens the modal. Bitrix via AssetManager includes the Vue build only on the required pages:

\Bitrix\Main\Page\Asset::getInstance()->addJs('/local/dist/modal-bundle.js'); 

SEO modal windows do not affect — their content is not indexed as primary by search engines. If the content is important for search, we duplicate links or structured data in

What's included in the work?

  • Audit of current pop-ups on the project and scenario agreement
  • Component prototyping with animations, focus trap, and accessibility
  • Data integration: REST API or custom AJAX controller
  • Build via Vite and inclusion via AssetManager
  • Usage documentation and source code handover
  • Caching setup for components with Vue
  • Your team training (upon request)

Development stages

  1. Analysis — inventory of existing modals, scenario definition, state agreement (open/closed/loading/error/success)
  2. Prototype — basic animation, scroll lock, close on overlay and Escape
  3. Integration — connection to REST API or AJAX controller, error handling
  4. Build and deploy — Vite build, inclusion via AssetManager on target pages

Checklist for modal windows before launch:

  • Overlay blocks background interaction
  • Escape closes the window
  • Focus trap works (Tab does not leave the modal)
  • Animations smooth, no jank
  • AJAX errors handled and displayed to user
  • Caching not broken (check in Composite mode)
  • Metric counters (Yandex.Metrica, Google Analytics) not lost

Estimated timelines

Task Time
One typical modal with a form 1-2 days
System of 3-5 related modals 3-5 days
Multi-step form with validation and CRM integration 5-10 days

Example pricing: single modal from $400, multi-step form from $1,500. Typical projects range from $2,500 to $5,000, with savings up to $5,000 on development compared to traditional methods.

The final cost is estimated individually after analyzing your project. Contact us — we will offer the optimal solution for your tasks. Get a consultation on integrating Vue.js into your Bitrix.