Tab
Create toggleable content panes using any of our navigation or list group components with this JavaScript plugin.
The Tab plugin works with nav components such as .nav-tabs and .nav-pills, and with list groups.
Nav tab
Add the required data attributes and id to your navigation to make it tabbable. Then, add the tab-content container with matching ids to each tab pane.
This is some placeholder content the Home tab’s associated content.
This is some placeholder content the Profile tab’s associated content.
This is some placeholder content the Contact tab’s associated content.
This is some placeholder content the Disabled tab’s associated content.
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="home-tab" data-bs-toggle="tab" data-bs-target="#home-tab-pane" type="button" role="tab" aria-controls="home-tab-pane" aria-selected="true">Home</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#profile-tab-pane" type="button" role="tab" aria-controls="profile-tab-pane" aria-selected="false">Profile</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="contact-tab" data-bs-toggle="tab" data-bs-target="#contact-tab-pane" type="button" role="tab" aria-controls="contact-tab-pane" aria-selected="false">Contact</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="disabled-tab" data-bs-toggle="tab" data-bs-target="#disabled-tab-pane" type="button" role="tab" aria-controls="disabled-tab-pane" aria-selected="false" disabled>Disabled</button>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="home-tab-pane" role="tabpanel" aria-labelledby="home-tab" tabindex="0">
<p>This is some placeholder content the <strong>Home tab’s</strong> associated content.</p>
</div>
<div class="tab-pane fade" id="profile-tab-pane" role="tabpanel" aria-labelledby="profile-tab" tabindex="0">
<p>This is some placeholder content the <strong>Profile tab’s</strong> associated content.</p>
</div>
<div class="tab-pane fade" id="contact-tab-pane" role="tabpanel" aria-labelledby="contact-tab" tabindex="0">
<p>This is some placeholder content the <strong>Contact tab’s</strong> associated content.</p>
</div>
<div class="tab-pane fade" id="disabled-tab-pane" role="tabpanel" aria-labelledby="disabled-tab" tabindex="0">
<p>This is some placeholder content the <strong>Disabled tab’s</strong> associated content.</p>
</div>
</div> With buttons
To help fit your needs, this works with <ul>-based markup, as shown above, or with any arbitrary “roll your own” markup. Note that if you’re using <nav>, you shouldn’t add role="tablist" directly to it, as this would override the element’s native role as a navigation landmark. Instead, switch to an alternative element (in the example below, a simple <div>) and wrap the <nav> around it.
<nav>
<div class="nav nav-tabs" id="nav-tab2" role="tablist">
<button class="nav-link active" id="nav-home-tab2" data-bs-toggle="tab" data-bs-target="#nav-home2" type="button" role="tab" aria-controls="nav-home2" aria-selected="true">Home</button>
<button class="nav-link" id="nav-profile-tab2" data-bs-toggle="tab" data-bs-target="#nav-profile2" type="button" role="tab" aria-controls="nav-profile2" aria-selected="false">Profile</button>
<button class="nav-link" id="nav-contact-tab2" data-bs-toggle="tab" data-bs-target="#nav-contact2" type="button" role="tab" aria-controls="nav-contact2" aria-selected="false">Contact</button>
<button class="nav-link" id="nav-disabled-tab2" data-bs-toggle="tab" data-bs-target="#nav-disabled2" type="button" role="tab" aria-controls="nav-disabled2" aria-selected="false" disabled>Disabled</button>
</div>
</nav>
<div class="tab-content" id="nav-tabContent2">
<div class="tab-pane fade show active" id="nav-home2" role="tabpanel" aria-labelledby="nav-home-tab2" tabindex="0">
<p>This is some placeholder content the <strong>Home tab’s</strong> associated content.</p>
</div>
<div class="tab-pane fade" id="nav-profile2" role="tabpanel" aria-labelledby="nav-profile-tab2" tabindex="0">
<p>This is some placeholder content the <strong>Profile tab’s</strong> associated content.</p>
</div>
<div class="tab-pane fade" id="nav-contact2" role="tabpanel" aria-labelledby="nav-contact-tab2" tabindex="0">
<p>This is some placeholder content the <strong>Contact tab’s</strong> associated content.</p>
</div>
<div class="tab-pane fade" id="nav-disabled2" role="tabpanel" aria-labelledby="nav-disabled-tab2" tabindex="0">
<p>This is some placeholder content the <strong>Disabled tab’s</strong> associated content.</p>
</div>
</div> Pills
The tabs plugin also works with pills.
This is some placeholder content the Home tab’s associated content.
This is some placeholder content the Profile tab’s associated content.
This is some placeholder content the Contact tab’s associated content.
This is some placeholder content the Disabled tab’s associated content.
<ul class="nav nav-pills" id="pills-tab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="pills-home-tab" data-bs-toggle="tab" data-bs-target="#pills-home" type="button" role="tab" aria-controls="pills-home" aria-selected="true">Home</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="pills-profile-tab" data-bs-toggle="tab" data-bs-target="#pills-profile" type="button" role="tab" aria-controls="pills-profile" aria-selected="false">Profile</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="pills-contact-tab" data-bs-toggle="tab" data-bs-target="#pills-contact" type="button" role="tab" aria-controls="pills-contact" aria-selected="false">Contact</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="pills-disabled-tab" data-bs-toggle="tab" data-bs-target="#pills-disabled" type="button" role="tab" aria-controls="pills-disabled" aria-selected="false" disabled>Disabled</button>
</li>
</ul>
<div class="tab-content" id="pills-tabContent">
<div class="tab-pane fade show active" id="pills-home" role="tabpanel" aria-labelledby="pills-home-tab" tabindex="0">
<p>This is some placeholder content the <strong>Home tab’s</strong> associated content.</p>
</div>
<div class="tab-pane fade" id="pills-profile" role="tabpanel" aria-labelledby="pills-profile-tab" tabindex="0">
<p>This is some placeholder content the <strong>Profile tab’s</strong> associated content.</p>
</div>
<div class="tab-pane fade" id="pills-contact" role="tabpanel" aria-labelledby="pills-contact-tab" tabindex="0">
<p>This is some placeholder content the <strong>Contact tab’s</strong> associated content.</p>
</div>
<div class="tab-pane fade" id="pills-disabled" role="tabpanel" aria-labelledby="pills-disabled-tab" tabindex="0">
<p>This is some placeholder content the <strong>Disabled tab’s</strong> associated content.</p>
</div>
</div> And with vertical pills. Ideally, for vertical tabs, you should also add aria-orientation="vertical" to the tab list container.
This is some placeholder content the Home tab’s associated content.
This is some placeholder content the Profile tab’s associated content.
This is some placeholder content the Disabled tab’s associated content.
This is some placeholder content the Messages tab’s associated content.
This is some placeholder content the Settings tab’s associated content.
<div class="d-flex align-items-start">
<div class="nav flex-column nav-pills me-3" id="v-pills-tab" role="tablist" aria-orientation="vertical">
<button class="nav-link active" id="v-pills-home-tab" data-bs-toggle="tab" data-bs-target="#v-pills-home" type="button" role="tab" aria-controls="v-pills-home" aria-selected="true">Home</button>
<button class="nav-link" id="v-pills-profile-tab" data-bs-toggle="tab" data-bs-target="#v-pills-profile" type="button" role="tab" aria-controls="v-pills-profile" aria-selected="false">Profile</button>
<button class="nav-link" id="v-pills-disabled-tab" data-bs-toggle="tab" data-bs-target="#v-pills-disabled" type="button" role="tab" aria-controls="v-pills-disabled" aria-selected="false" disabled>Disabled</button>
<button class="nav-link" id="v-pills-messages-tab" data-bs-toggle="tab" data-bs-target="#v-pills-messages" type="button" role="tab" aria-controls="v-pills-messages" aria-selected="false">Messages</button>
<button class="nav-link" id="v-pills-settings-tab" data-bs-toggle="tab" data-bs-target="#v-pills-settings" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false">Settings</button>
</div>
<div class="tab-content" id="v-pills-tabContent">
<div class="tab-pane fade show active" id="v-pills-home" role="tabpanel" aria-labelledby="v-pills-home-tab" tabindex="0">
<p>This is some placeholder content the <strong>Home tab’s</strong> associated content.</p>
</div>
<div class="tab-pane fade" id="v-pills-profile" role="tabpanel" aria-labelledby="v-pills-profile-tab" tabindex="0">
<p>This is some placeholder content the <strong>Profile tab’s</strong> associated content.</p>
</div>
<div class="tab-pane fade" id="v-pills-disabled" role="tabpanel" aria-labelledby="v-pills-disabled-tab" tabindex="0">
<p>This is some placeholder content the <strong>Disabled tab’s</strong> associated content.</p>
</div>
<div class="tab-pane fade" id="v-pills-messages" role="tabpanel" aria-labelledby="v-pills-messages-tab" tabindex="0">
<p>This is some placeholder content the <strong>Messages tab’s</strong> associated content.</p>
</div>
<div class="tab-pane fade" id="v-pills-settings" role="tabpanel" aria-labelledby="v-pills-settings-tab" tabindex="0">
<p>This is some placeholder content the <strong>Settings tab’s</strong> associated content.</p>
</div>
</div>
</div> List groups
The tab plugin also works with list groups. Add data-bs-toggle="tab" to each .list-group-item and pair them with .tab-pane elements.
<div class="row">
<div class="col-4">
<div class="list-group" id="list-tab" role="tablist">
<a class="list-group-item list-group-item-action active" id="list-home-list" data-bs-toggle="tab" href="#list-home" role="tab" aria-controls="list-home">Home</a>
<a class="list-group-item list-group-item-action" id="list-profile-list" data-bs-toggle="tab" href="#list-profile" role="tab" aria-controls="list-profile">Profile</a>
<a class="list-group-item list-group-item-action" id="list-messages-list" data-bs-toggle="tab" href="#list-messages" role="tab" aria-controls="list-messages">Messages</a>
<a class="list-group-item list-group-item-action" id="list-settings-list" data-bs-toggle="tab" href="#list-settings" role="tab" aria-controls="list-settings">Settings</a>
</div>
</div>
<div class="col-8">
<div class="tab-content" id="list-tabContent">
<div class="tab-pane fade show active" id="list-home" role="tabpanel" aria-labelledby="list-home-list">
<p>Some placeholder content in a paragraph relating to "Home".</p>
</div>
<div class="tab-pane fade" id="list-profile" role="tabpanel" aria-labelledby="list-profile-list">
<p>Some placeholder content in a paragraph relating to "Profile".</p>
</div>
<div class="tab-pane fade" id="list-messages" role="tabpanel" aria-labelledby="list-messages-list">
<p>Some placeholder content in a paragraph relating to "Messages".</p>
</div>
<div class="tab-pane fade" id="list-settings" role="tabpanel" aria-labelledby="list-settings-list">
<p>Some placeholder content in a paragraph relating to "Settings".</p>
</div>
</div>
</div>
</div> Accessibility
Dynamic tabbed interfaces, as described in the ARIA Authoring Practices Guide tabs pattern, require role="tablist", role="tab", role="tabpanel", and additional aria- attributes in order to convey their structure, functionality, and current state to users of assistive technologies (such as screen readers). As a best practice, we recommend using <button> elements for the tabs, as these are controls that trigger a dynamic change, rather than links that navigate to a new page or location.
In line with the ARIA Authoring Practices pattern, only the currently active tab receives keyboard focus. When the JavaScript plugin is initialized, it will set tabindex="-1" on all inactive tab controls. Once the currently active tab has focus, the cursor keys activate the previous/next tab. The Home and End keys activate the first and last tabs, respectively. The plugin will change the roving tabindex accordingly. However, note that the JavaScript plugin does not distinguish between horizontal and vertical tab lists when it comes to cursor key interactions: regardless of the tab list’s orientation, both the up and left cursor go to the previous tab, and down and right cursor go to the next tab.
In general, to facilitate keyboard navigation, it’s recommended to make the tab panels themselves focusable as well, unless the first element containing meaningful content inside the tab panel is already focusable. The JavaScript plugin does not try to handle this aspect—where appropriate, you’ll need to explicitly make your tab panels focusable by adding tabindex="0" in your markup.
The tab JavaScript plugin does not support tabbed interfaces that contain menus, as these cause both usability and accessibility issues. From a usability perspective, the fact that the currently displayed tab’s trigger element is not immediately visible (as it’s inside the closed menu) can cause confusion. From an accessibility point of view, there is currently no sensible way to map this sort of construct to a standard WAI ARIA pattern, meaning that it cannot be easily made understandable to users of assistive technologies.
Usage
Via data attributes
You can activate a tab navigation without writing any JavaScript by simply specifying data-bs-toggle="tab" on an element.
| Attribute | Description |
|---|---|
data-bs-toggle="tab" | Activates tab navigation on the trigger. |
data-bs-target | CSS selector for the tab pane to show (must pair with a corresponding .tab-pane). |
<!-- Nav tabs -->
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="home-tab" data-bs-toggle="tab" data-bs-target="#home" type="button" role="tab" aria-controls="home" aria-selected="true">Home</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#profile" type="button" role="tab" aria-controls="profile" aria-selected="false">Profile</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="messages-tab" data-bs-toggle="tab" data-bs-target="#messages" type="button" role="tab" aria-controls="messages" aria-selected="false">Messages</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="settings-tab" data-bs-toggle="tab" data-bs-target="#settings" type="button" role="tab" aria-controls="settings" aria-selected="false">Settings</button>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div class="tab-pane active" id="home" role="tabpanel" aria-labelledby="home-tab" tabindex="0">...</div>
<div class="tab-pane" id="profile" role="tabpanel" aria-labelledby="profile-tab" tabindex="0">...</div>
<div class="tab-pane" id="messages" role="tabpanel" aria-labelledby="messages-tab" tabindex="0">...</div>
<div class="tab-pane" id="settings" role="tabpanel" aria-labelledby="settings-tab" tabindex="0">...</div>
</div>Via JavaScript
Enable tabbable tabs via JavaScript (each tab needs to be activated individually):
const triggerTabList = document.querySelectorAll('#myTab button')
triggerTabList.forEach(triggerEl => {
const tabTrigger = new bootstrap.Tab(triggerEl)
triggerEl.addEventListener('click', event => {
event.preventDefault()
tabTrigger.show()
})
})You can activate individual tabs in several ways:
const triggerEl = document.querySelector('#myTab button[data-bs-target="#profile"]')
bootstrap.Tab.getInstance(triggerEl).show() // Select tab by name
const triggerFirstTabEl = document.querySelector('#myTab li:first-child button')
bootstrap.Tab.getInstance(triggerFirstTabEl).show() // Select first tabFade effect
To make tabs fade in, add .fade to each .tab-pane. The first tab pane must also have .show to make the initial content visible.
<div class="tab-content">
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab" tabindex="0">...</div>
<div class="tab-pane fade" id="profile" role="tabpanel" aria-labelledby="profile-tab" tabindex="0">...</div>
<div class="tab-pane fade" id="messages" role="tabpanel" aria-labelledby="messages-tab" tabindex="0">...</div>
<div class="tab-pane fade" id="settings" role="tabpanel" aria-labelledby="settings-tab" tabindex="0">...</div>
</div>Methods
All API methods are asynchronous and start a transition. They return to the caller as soon as the transition is started, but before it ends. In addition, a method call on a transitioning component will be ignored. Learn more in our JavaScript docs.
Activates your content as a tab element.
You can create a tab instance with the constructor, for example:
const bsTab = new bootstrap.Tab('#myTab')| Method | Description |
|---|---|
dispose | Destroys an element’s tab. |
getInstance | Static method which allows you to get the tab instance associated with a DOM element, you can use it like this: bootstrap.Tab.getInstance(element). |
getOrCreateInstance | Static method which returns a tab instance associated to a DOM element or create a new one in case it wasn’t initialized. You can use it like this: bootstrap.Tab.getOrCreateInstance(element). |
show | Selects the given tab and shows its associated pane. Any other tab that was previously selected becomes unselected and its associated pane is hidden. Returns to the caller before the tab pane has actually been shown (i.e. before the shown.bs.tab event occurs). |
Events
When showing a new tab, the events fire in the following order:
hide.bs.tab(on the current active tab)show.bs.tab(on the to-be-shown tab)hidden.bs.tab(on the previous active tab, the same one as for thehide.bs.tabevent)shown.bs.tab(on the newly-active just-shown tab, the same one as for theshow.bs.tabevent)
If no tab was already active, then the hide.bs.tab and hidden.bs.tab events will not be fired.
| Event type | Description |
|---|---|
hide.bs.tab | This event fires when a new tab is to be shown (and thus the previous active tab is to be hidden). Use event.target and event.relatedTarget to target the current active tab and the new soon-to-be-active tab, respectively. |
hidden.bs.tab | This event fires after a new tab is shown (and thus the previous active tab is hidden). Use event.target and event.relatedTarget to target the previous active tab and the new active tab, respectively. |
show.bs.tab | This event fires on tab show, but before the new tab has been shown. Use event.target and event.relatedTarget to target the active tab and the previous active tab (if available) respectively. |
shown.bs.tab | This event fires on tab show after a tab has been shown. Use event.target and event.relatedTarget to target the active tab and the previous active tab (if available) respectively. |
const tabEl = document.querySelector('button[data-bs-toggle="tab"]')
tabEl.addEventListener('shown.bs.tab', event => {
event.target // newly activated tab
event.relatedTarget // previous active tab
})