Current version 22.5.0

Tabs Beta

import { Tg2TabsComponent, Tg2TabComponent } from '@trademe/tangram';

<tg2-tabs> wraps one or more <tg2-tab> elements. Each tab's content is projected directly inside it — there is no separate heading or content wrapper element, unlike the tg-tab / tg-tab-heading / tg-tab-content triple inside tg-tabs.

Give every tablist an accessible name with ariaLabel — required whenever a page has more than one set of tabs. [(selected)] is the zero-based index of the open tab; if it points outside the tab list or at a disabled tab, the component selects the first enabled tab and emits the corrected index through selectedChange, so a two-way binding always matches what is on screen.

Using tg2-tabs on a page that still has the old Tangram design? Set theme="tg-light".

Sizes

Three sizes: small (14px text, 32px tall, default), medium (16px text, 40px tall) and large (18px text, 48px tall). Heights match tg2Button at the same size.

Small Default

First tab content
Second tab content
Third tab content

Medium

First tab content
Second tab content
Third tab content

Large

First tab content
Second tab content
Third tab content

Icons

iconLeft / iconRight take a Tangram icon name, rendered at 16px (small and medium) or 24px (large).

Small Default

Panel content
More content

Medium

Panel content
More content

Large

Panel content
More content

Disabled tabs

A disabled tab is removed from the Tab and arrow-key sequences (Home and End skip over it too) and cannot be activated by click.

Second tab disabled

Panel content
Unavailable
More content

Overflow

When the tabs are wider than their container the strip scrolls horizontally and fades at the clipped edge, the same treatment as tg-tabs. Arrow keys, clicks and a bound selected value keep the focused or selected tab in view by scrolling the strip; the component itself never scrolls the page.

Twelve tabs

Overview content
Specifications content
Reviews content
Questions content
Shipping content
Returns content
Warranty content
Seller content
Similar items content
Delivery options content
Payment methods content
Seller policies content

Keyboard navigation

  • Tab / Shift+Tab — step through each tab in order, then on into the selected panel, as in tg-tabs
  • ArrowLeft / ArrowRight — move focus between tabs from whichever tab has focus, wrapping at the ends, without changing the selected tab
  • Home / End — move focus to the first / last enabled tab
  • Enter / Space — select the focused tab
  • Disabled tabs are skipped automatically
  • Activation is manual, as in tg-tabs: arrowing onto a tab shows its focus ring but leaves the panel unchanged
  • An overflowing strip scrolls to keep the focused tab in view

Server-side rendering and hydration

Server-side rendered. The default. The user only sees the selected tab's content, but every tab's content is in the initial HTML, so search engines can index all of it:

<tg2-tabs
    ariaLabel="Product details"
    [(selected)]="selectedIndex">
    <tg2-tab label="Details">
        Panel one content
    </tg2-tab>
    <tg2-tab label="Reviews">
        Panel two content
    </tg2-tab>
    <tg2-tab
        label="Shipping"
        disabled>
        Panel three content
    </tg2-tab>
</tg2-tabs>

Hydrated. Use it for larger components to improve initial page performance. The server sends the placeholder and the component loads in the browser once its tab is active, even if that is the first tab. Gate it on the tab's active signal:

<tg2-tabs [(selected)]="selectedTab">
    <tg2-tab label="Overview">
        <app-listing-overview />
    </tg2-tab>
    <tg2-tab #historyTab label="Price history">
        @defer (when historyTab.active()) {
            <app-price-history-chart />
        } @placeholder {
            <p>Chart loading…</p>
        }
    </tg2-tab>
</tg2-tabs>

There is no lazy input. @defer in your template is the only option.

Proudly made by © 2026 Trade Me Limited