Slider and Carousel Development on Vue.js for 1C-Bitrix
On most Bitrix projects, sliders are implemented via Swiper.js with jQuery initialization in template.php. This works for simple cases. Problems arise when you need to: dynamically load slides from the cart or wishlist, synchronize multiple carousels (main + thumbnails), manage the active slide state from another component. Vue.js provides the reactivity that jQuery simply doesn't have.
Why Vue Instead of Plain Swiper in Bitrix
Swiper.js is an excellent library for static content. But when a slider is tied to data — a cart, wishlist, catalog filters — the jQuery approach requires manual DOM-state synchronization. Added a product to favorites → you need to call swiper.update(), recalculate slides, update the counter. On AJAX error — roll back the visual state manually.
Vue solves this through reactivity: the slides array in ref() is the source of truth. The array changes — Vue re-renders the DOM. Swiper (if its gestures are needed) is mounted on top of the already-rendered Vue output via onMounted.
Main integration patterns:
-
Vue + native Swiper — Vue manages data, Swiper adds touch gestures and transitions. Initialize Swiper in
onMounted, destroy inonBeforeUnmount, recreate on array change viawatch -
Fully in Vue — for carousels without complex physics (products, reviews, banners). CSS transitions +
transform: translateX()via computed styles. Fewer dependencies, full control - vue3-carousel — a ready-made library, well-typed, supports infinite loop, autoplay, responsive breakpoints. Saves you from writing transition logic for standard use cases
Typical Bitrix Scenarios
Main banner with content from an infoblock. Slides come from CIBlockElement::GetList in result_modifier.php, passed to Vue via data-slides JSON or via window.BX_STATE. Autoplay, pause on hover, slide counter, dot and arrow navigation.
Product carousel (best sellers, new arrivals, related products). Data from bitrix:catalog.section or via REST /bitrix/api.php. The product card is a nested Vue component with an "Add to cart" button that updates cartStore via Pinia. Button state change (adding → added) without a page reload.
Product gallery with synchronization. Two linked sliders: large images + thumbnail strip. Clicking a thumbnail — the main slider jumps to the corresponding image. Via provide/inject or Pinia — a single activeIndex shared by both components. Click-to-zoom via CSS transform without a separate library.
Reviews slider. Content from the "Reviews" infoblock, lazy-loading avatar images via IntersectionObserver. Infinite scroll — when the end of the array is reached, it's cloned in a reactive computed.
Case: Synchronized Gallery with Zoom for a Product Page
A furniture store, 15–40 photos per product, requirements: main image + thumbnail strip + hover zoom + mobile swipe support.
The standard Bitrix component bitrix:catalog.element rendered only a single image without a gallery. Adding a jQuery lightbox caused conflicts with Swiper, which was already used for related products.
Solution: Vue component ProductGallery, mounted on #product-gallery-root. Image data is passed via window.BX_STATE.gallery — an array of {thumb, full, alt} objects, assembled in PHP from CIBlockElement::GetList selecting DETAIL_PICTURE and MORE_PHOTO.
// Synchronization via a single reactive index
const activeIndex = ref(0);
// Zoom via CSS custom properties
const zoomStyle = computed(() => ({
'--zoom-x': `${zoomPos.x}%`,
'--zoom-y': `${zoomPos.y}%`
}));
Mobile swipe — via @vueuse/core useSwipe. Transitions between images — CSS opacity transition 200ms. Thumbnails — horizontal scroll via scrollIntoView on activeIndex change. Hover zoom — CSS transform: scale(2) with transform-origin based on mouse position via a mousemove handler.
Result: one dependency (@vueuse/core), 280 lines of code, a full replacement for three jQuery plugins. Page load time unchanged — the 18 KB gzipped bundle is only loaded on the product page.
Build and Integration in Bitrix
Sliders are isolated Vue applications, each mounted on its own root element. Vite builds separate entry points for each component:
// vite.config.js
export default {
build: {
rollupOptions: {
input: {
productGallery: 'src/product-gallery/main.js',
homeBanner: 'src/home-banner/main.js',
reviewsSlider: 'src/reviews-slider/main.js',
}
}
}
}
Connected via \Bitrix\Main\Page\Asset only on the pages that need them. Components are unaware of each other; conflicts are eliminated. If Pinia is needed in multiple components on the same page — a single global store is created and connected as a singleton via window.__pinia.
Stages and Timelines
| Slider type | Estimated timeline |
|---|---|
| Banner with infoblock content | 1-2 days |
| Product carousel with cart | 2-4 days |
| Synchronized gallery with zoom | 3-6 days |
| A set of 3-4 linked sliders | 5-10 days |
Timelines increase for non-standard animations, complex breakpoints, and accessibility requirements (ARIA live regions for screen readers).







