Vue.js Tabs and Accordions for 1C-Bitrix

Development of tabs and accordions on Vue.js for 1C-Bitrix is an architectural decision that directly affects performance, SEO indexing, and UX. An incorrect approach leads to HTML duplication, slow loading, and inability to deep-link. We create components that save up to 30% development time and re

Our competencies:

Frequently Asked Questions

Development of tabs and accordions on Vue.js for 1C-Bitrix is an architectural decision that directly affects performance, SEO indexing, and UX. An incorrect approach leads to HTML duplication, slow loading, and inability to deep-link. We create components that save up to 30% development time and reduce HTML code by 2-3 times.

In a standard Bitrix template with 5 tabs, the DOM renders 5 times more content than needed — increasing load time by 200–500 ms on mobile devices. jQuery implementations via .show()/.hide() do not update the URL, making deep linking impossible. Our stack: Vue.js, PHP 8.1+, information blocks v2.0, tagged caching. We have specialized in this combination for over 6 years and completed 45+ projects with custom interfaces.

How to implement lazy loading for tabs?

Tabs are built with a pair of components: TabGroup manages the active state, TabPanel renders the content of the active tab. The key decision is how to store the active tab:

  • ref() inside the component — if URL is not needed
  • useRoute from Vue Router — for SPA pages with full routing
  • URLSearchParams via native history.pushState — for Bitrix pages without Vue Router when deep linking is needed
// Сохранение активного таба в URL без Vue Router const activeTab = ref(new URLSearchParams(location.search).get('tab') || 'description'); watch(activeTab, (tab) => { const url = new URL(location.href); url.searchParams.set('tab', tab); history.replaceState({}, '', url); }); 

This allows users to copy a link to a specific tab — critical for product cards with 'Specifications', 'Reviews', 'Delivery' tabs.

Accordions require smooth height animation, which is non-trivial in CSS: height: auto does not animate. Solution via Vue <Transition> with JavaScript hooks:

function onEnter(el) { el.style.height = '0'; el.offsetHeight; // force reflow el.style.height = el.scrollHeight + 'px'; } function onAfterEnter(el) { el.style.height = 'auto'; } function onLeave(el) { el.style.height = el.scrollHeight + 'px'; el.offsetHeight; el.style.height = '0'; } 

This gives native height animation without libraries, working with any content inside. Code savings compared to jQuery solutions — up to 50%.

How to integrate tabs with Bitrix data?

Tab content from information blocks. Product card with tabs: 'Description' — DETAIL_TEXT from b_iblock_element, 'Specifications' — properties from b_iblock_element_property, 'Reviews' — data from the reviews information block. In result_modifier.php, an array is formed:

$arResult['TABS'] = [ ['id' => 'description', 'title' => 'Описание', 'content' => $arResult['DETAIL_TEXT']], ['id' => 'props', 'title' => 'Характеристики', 'props' => $arResult['DISPLAY_PROPERTIES']], ['id' => 'reviews', 'title' => 'Отзывы', 'lazy' => true, 'endpoint' => '/api/reviews/?id=' . $arResult['ID']], ]; 

The 'Reviews' tab is marked with lazy: true — content is loaded via AJAX only on first open. The loading state of the component is a spinner during the request.

Accordion FAQ from information block. The bitrix:news.list component template outputs questions and answers. The Vue component mounts onto the container, parses existing HTML via querySelectorAll (progressive enhancement) or receives data via data-items JSON.

Why choose Vue over jQuery for tabs?

Comparison:

Feature jQuery Vue.js
HTML code Duplicated blocks for each state Single template with reactive data
Performance Always renders all content Only active tab rendered (v-if)
Maintainability Hard to maintain with 5+ tabs Easy to extend: add a tab — new entry in array
URL synchronization Requires additional workarounds Built-in via URLSearchParams

Vue solution reduces HTML code size by 2-3 times and simplifies maintenance. According to our tests, switching between 10 tabs on Vue is 25% faster than on jQuery.

Case study: tabs on a pricing page with dynamic period switching

In one project, our client, a SaaS product on Bitrix, faced a problem: the pricing page with tabs 'Monthly Payment' / 'Yearly Payment'. When switching the tab, prices on all tariff cards had to change without page reload.

jQuery solution required duplicating all HTML with prices — one set for monthly prices, a second for yearly, and toggling visibility. With 6 tariffs — 12 HTML blocks, hard to maintain.

Vue solution: one component PricingTabs, data from window.BX_STATE.pricing:

const period = ref('monthly'); // 'monthly' | 'yearly' const plans = computed(() => rawPlans.value.map(plan => ({ ...plan, price: period.value === 'yearly' ? Math.round(plan.yearlyPrice / 12) : plan.monthlyPrice, badge: period.value === 'yearly' ? `Экономия ${plan.yearlySaving}%` : null })) ); 

Prices are stored in JSON, computed reactively when the period changes. Price change animation — <Transition name="price-flip"> with CSS rotateX. Data in PHP is filled from the 'Tariffs' information block with fields MONTHLY_PRICE, YEARLY_PRICE. Development took 2 days. Contact us to discuss a similar case for your project.

How to ensure accessibility and SEO for tabs?

Tabs without ARIA is a common mistake. Correct markup:

<div role="tablist"> <button role="tab" :aria-selected="activeTab === 'desc'" :aria-controls="'panel-desc'"> Описание </button> </div> <div role="tabpanel" id="panel-desc" :hidden="activeTab !== 'desc'"> <!-- контент --> </div> 

The hidden attribute instead of v-show with display: none — screen readers correctly ignore hidden panels. Keyboard navigation between tabs — ArrowLeft/ArrowRight via onKeydown.

SEO aspect: search engines index tab content if it is present in HTML. With v-if (lazy rendering), the first tab is rendered server-side via the Bitrix template, the rest load via AJAX. For Googlebot this is acceptable — it executes JavaScript but with a delay. For critical SEO content (product specifications) — always render in HTML, hide via hidden. Read more about ARIA at Wikipedia: ARIA and MDN documentation.

Common mistakes when developing tabs and accordions
  • Using v-show instead of v-if for hidden tabs — content remains in DOM and slows rendering. Choose v-if for lazy loading.
  • Forgot hidden attribute: screen reader may read hidden panels. Always add :hidden="activeTab !== 'desc'".
  • Accordion animation via CSS height: doesn't work for auto. Use Vue Transition JavaScript hooks.

What's included in development and timeline?

  • Analysis: determining data sources, prototyping components
  • Design: architecture of Vue components, contracts with backend
  • Implementation: development of tabs/accordions on Vue, integration with information blocks and APIs
  • Testing: cross-browser, accessibility, performance
  • Documentation: component description, usage examples
  • Support: 3-month warranty
Component Approximate Timeline
Tabs with static content + URL saving 1-2 days
Tabs with lazy loading 2-3 days
Accordion with animation 1 day
Linked tabs + dynamic data (as in pricing example) 3-5 days

Timelines are determined by the number of data sources, animation requirements, and complexity of integration with Bitrix components. Get a consultation for your project — we'll select the optimal architectural solution.

Order turnkey tab development — we'll prepare a commercial proposal within 1 business day. Contact us to discuss the details of your project.