How to add and reorder product tabs
Introduction
Product page tabs are built from layout XML blocks, so a child theme or feature module can add, reorder, or remove tabs without overriding any template.
The structure on the product page is:
product.info.tabs wrapper block (alias: tabs)
├── product.info.tabs.attributes attribute-based tabs, first by default
│ └── product.info.tabs.attributes.tab
└── product.info.tabs.cms global CMS block tabs
└── product.info.tabs.cms.tabEvery tab renders through the shared partial Magento_Catalog::product/view/tabs/tab.phtml, which draws the collapsible accordion item. The .tab child blocks above define which template renders a single tab, so a project can replace the tab markup by re-pointing one template in layout XML.
All examples below go into the child theme's Magento_Catalog/layout/catalog_product_view.xml.
Reorder the tab groups
Move the global CMS tabs before the attribute tabs:
<move element="product.info.tabs.cms" destination="product.info.tabs"
before="product.info.tabs.attributes"/>Add a static tab
The shared partial accepts its data through layout arguments, so a simple text tab needs no PHP at all:
<referenceBlock name="product.info.tabs">
<block name="product.info.tabs.size-guide"
template="Magento_Catalog::product/view/tabs/tab.phtml"
after="product.info.tabs.attributes">
<arguments>
<argument name="title" xsi:type="string" translate="true">Size Guide</argument>
<argument name="content" xsi:type="string">Our sizes run true to fit.</argument>
<argument name="collapsed_by_default" xsi:type="boolean">true</argument>
</arguments>
</block>
</referenceBlock>The partial takes three arguments:
- title - the accordion heading, escaped by the partial
- content - the tab body HTML, printed as-is, so the caller is responsible for escaping
- collapsed_by_default - whether the tab starts closed
A tab with empty content renders nothing, so a conditional tab can simply produce no output when it does not apply.
Add a tab with custom logic
For a tab that needs its own data source, add a block with your own template and build the content there. Render the item through the shared partial so the tab matches the rest of the accordion:
<referenceBlock name="product.info.tabs">
<block name="product.info.tabs.downloads"
template="Vendor_Module::product/view/tabs/downloads.phtml"
before="product.info.tabs.cms">
<block name="product.info.tabs.downloads.tab" as="tab"
template="Magento_Catalog::product/view/tabs/tab.phtml"/>
</block>
</referenceBlock><?php $tabBlock = $block->getChildBlock('tab'); ?>
<?php if ($tabBlock instanceof \Magento\Framework\View\Element\Template): ?>
<?= /** @noEscape */ $tabBlock->setData([
'title' => __('Downloads'),
'content' => $downloadListHtml,
'collapsed_by_default' => true,
])->toHtml() ?>
<?php endif; ?>Note
The accordion behaviour comes from the
initProductTabAlpine component, which is registered once byMagento_Catalog::product/view/tabs.phtml. The partial only works when its block sits insideproduct.info.tabs.
Remove a tab group
A child theme that does not use global CMS tabs can remove the whole group:
<referenceBlock name="product.info.tabs.cms" remove="true"/>Related configuration
- Attribute-based tabs are controlled per attribute in the admin. See Attributes.
- Global CMS block tabs are managed under
Stores > Configuration > Magebit > Theme > Product Tabs. See Product Tabs.