Quick answer: A Materialize tab set is a ul.tabs whose each li.tab contains an anchor pointing at a div#id pane. Materialize 1.0.0 does not initialise tabs for you — you must call M.Tabs.init(document.querySelectorAll('.tabs')), or run M.AutoInit() to cover every component on the page. Add the tabs-fixed-width class to spread the tabs evenly across the width. The usual mistake is forgetting the init call, which leaves the tabs styled but dead.
Part of our Materialize series. If the framework is not on your page yet, run the Materialize CSS tutorial first for the CDN setup.
Working tabs in one block
The structure is a list of triggers followed by the content panes. Each anchor’s href matches a pane id, which is how Materialize knows what to show.
<ul class="tabs">
<li class="tab col s6"><a class="active" href="#overview">Overview</a></li>
<li class="tab col s6"><a href="#pricing">Pricing</a></li>
</ul>
<div id="overview" class="col s12">Overview content.</div>
<div id="pricing" class="col s12">Pricing content.</div>
document.addEventListener('DOMContentLoaded', function () {
M.Tabs.init(document.querySelectorAll('.tabs'), {
swipeable: true,
duration: 300,
});
});
The active class on the first anchor is the initial state. Because each tab is a real anchor, Materialize also updates the URL hash, which means a reload lands on the same tab — useful for shareable links. The panes themselves do not need any special class; the col s12 classes only apply if you are placing them inside a row of the grid.
Classes and options
| Class or option | Where | Effect |
|---|---|---|
| tabs-fixed-width | on the ul | Every tab shares an equal width |
| col s6 / s4 | on each li | Sets the width of a tab on the 12-column grid |
| active | on one anchor | Marks the tab open on load |
| swipeable | JS option | Allows swiping between panes on touch devices |
| duration | JS option | Transition length in milliseconds |
| responsiveThreshold | JS option | Switches to the mobile layout below this width |
| Component | How options are shown | Choose it when |
|---|---|---|
| Tabs | All headings visible at once | Two to six short panels the user compares |
| Dropdown | One label, options on click | Many options, or a single action menu |
| Carousel | One panel, advanced by control | Ordered, story-like content |
| Collapsible | Stacked headers, open in place | Long content on small screens |
Fixed-width versus responsive
By default Materialize tabs size themselves to their content, so two short tabs sit at the left while four long ones nearly fill the bar. Add tabs-fixed-width when you want a predictable, evenly divided strip. On narrow screens the plugin can switch to a scrollable “swipe” layout; set responsiveThreshold to the breakpoint where that should happen.
<ul class="tabs tabs-fixed-width">
<li class="tab"><a href="#a">One</a></li>
<li class="tab"><a href="#b">Two</a></li>
<li class="tab"><a href="#c">Three</a></li>
</ul>
Switching tabs from code
You can select a tab programmatically, which is handy after a form save or when a deep link needs to open a specific pane. The instance exposes a select method that takes the pane id.
const instance = M.Tabs.getInstance(document.querySelector('.tabs'));
instance.select('pricing');
// Recalculate indicator positions after the tab bar's size changes
instance.updateTabWidth();
Call updateTabWidth() whenever the tab bar is revealed after being hidden — for example inside a modal or a sidenav. Materialize measures tab widths at init time, so a bar that started with a width of zero draws its underline in the wrong place until you ask it to recalculate.
Common mistakes
- No initialisation. Tabs are inert until
M.Tabs.init()orM.AutoInit()runs. - href and id out of sync. The anchor
href="#pricing"needs a pane withid="pricing". - Underline in the wrong place. Reveal the tab bar before initialising, or call
updateTabWidth()afterwards. - Anchors that navigate away. Use a hash href, not a full URL, or the page reloads instead of switching tabs.
- Missing
active. With no active anchor the first pane is shown but the indicator has nothing to sit under.
Tabs behave much like a dropdown menu or an carousel in that they all switch between named panels — the difference is that tabs keep every option visible at once. For the button styling used inside panes, see the Materialize buttons guide.
Building a settings screen with several tabbed sections? Ampersand Academy teaches front-end development one-to-one, using your own project as the curriculum.
Frequently asked questions
Why are my Materialize tabs not switching?
The tabs were probably never initialised. Call M.Tabs.init on the tab elements, or run M.AutoInit, and confirm each anchor href matches the id of its content pane.
How do I make Materialize tabs equal width?
Add the tabs-fixed-width class to the ul. Every tab then takes an equal share of the bar instead of sizing to its own label length.
How do I open a specific Materialize tab on page load?
Put the active class on the anchor for that tab, or link to the page with the matching hash. On initialization you can also call the instance select method with the pane id.
Why is the Materialize tab indicator in the wrong position?
The bar was likely measured while hidden or before the layout settled. Call updateTabWidth on the tab instance after it becomes visible to recalculate the indicator.
Can Materialize tabs be swiped on mobile?
Yes. Set the swipeable option to true when initialising. Materialize then allows dragging horizontally between panes on touch devices.

