Vue.js Tabs and Accordions for 1C-Bitrix

Standard tabs and accordions in Bitrix often slow down the site and harm SEO by duplicating content and lacking deep linking support. We build custom Vue components that address these issues, implementing lazy loading and URL integration. Our team handles the entire cycle from architecture to deployment, delivering a reliable solution that scales with your business.

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.