Tabs
The TabGroup
component and its children (TabList
, Tab
, TabPanel
) create a fully accessible, unstyled tabbed interface. Inspired by Headless UI’s Tabs, but built natively for Angular.
It gives you complete control over markup and styling while providing accessible keyboard navigation and ARIA roles.
When to use it
Use TabGroup
when you need a tabbed UI that is:
- Fully customizable in layout and appearance
- Accessible with screen readers and keyboard navigation
- Controlled via Angular templateRefs or DI
- Compatible with reactive or template-driven forms
Common use cases:
- Sectioned content areas
- Multi-step forms
- Data toggles
Anatomy
A complete TabGroup
is composed of:
TabGroup
– The root container that manages the tab stateTabList
– The container for tabsTab
– Each clickable tab optionTabPanel
– The content area for each tab
Features
- ✅ Accessible by default (ARIA attributes)
- ✅ Tailwind-friendly (no styles imposed)
- ✅ Multiple selectors supported:
<TabGroup>
<div ngxTabGroup>
<ngx-headlessui-tab-group>
- ✅ Works with both
#templateRefs
and Angular DI (TabsContextService
) - ✅ Supports multiple instances per page
Installation
TabGroup
ships as part of the @ngx-headless/ui
by default. Install if you haven't already.
npm install @ngx-headless/ui
Import the components directly:
import {
TabGroupComponent,
TabListComponent,
TabComponent,
TabPanelComponent,
} from "@ngx-headless/ui";
Usage Examples
Template Reference — Basic Usage
<TabGroup #tabs="ngxTabGroup" [defaultIndex]="0">
<TabList>
<Tab [index]="0">Tab 1</Tab>
<Tab [index]="1">Tab 2</Tab>
</TabList>
<TabPanel [index]="0">Content 1</TabPanel>
<TabPanel [index]="1">Content 2</TabPanel>
</TabGroup>
Template Reference — Access from Parent Component
@ViewChild('tabs') tabsGroup!: TabGroupComponent;
ngAfterViewInit() {
this.tabsGroup.selectTab(0); // 0 or any other index
}
Accessibility
This component handles:
role="tablist"
on the tab listrole="tab"
andaria-selected
on each tabrole="tabpanel"
andhidden
on each panel- Keyboard navigation:
←
,→
,Home
,End
to move focus
Animations
TabPanel
can be animated freely using Angular’s built-in animation system.
Angular supports entry/exit transitions using @trigger
bindings and the :enter
/:leave
lifecycle hooks.
👉 See Angular Animations Guide
Example
import { trigger, transition, style, animate } from "@angular/animations";
@Component({
animations: [
trigger("fadeInOut", [
transition(":enter", [
style({ opacity: 0 }),
animate("200ms ease-out", style({ opacity: 1 })),
]),
transition(":leave", [
animate("150ms ease-in", style({ opacity: 0 })),
]),
]),
],
})
export class ExampleComponent {}
<TabPanel [index]="0" *ngIf="tabsGroup.getSelectedIndex() === 0" @fadeInOut>
Panel content goes here.
</TabPanel>
✅ You now have full control over entrance and exit behavior of the panel.
Component API
TabGroupComponent
Input | Type | Description |
---|---|---|
selectedIndex? | number | Controls the selected tab (controlled) |
defaultIndex? | number | Initial selected tab (uncontrolled) |
class | string | Classes applied to the wrapper |
Output | Type | Description |
---|---|---|
selectedIndexChange | EventEmitter<number> | Emits when selected index changes |
Method | Returns | Description |
---|---|---|
selectTab(index) | void | Sets the selected tab by index |
getSelectedIndex() | `number | null` |
TabListComponent
HostBinding | Type | Description |
---|---|---|
role | 'tablist' | ARIA role automatically assigned |
TabComponent
Input | Type | Description |
---|---|---|
index | number | The index this tab represents |
disabled? | boolean | Whether the tab is disabled |
HostBinding | Type | Description |
---|---|---|
role | 'tab' | ARIA role automatically assigned |
aria-selected | boolean | Whether this tab is currently selected |
tabindex | number | Controls keyboard focus |
TabPanelComponent
Input | Type | Description |
---|---|---|
index | number | The index this panel represents |
HostBinding | Type | Description |
---|---|---|
role | 'tabpanel' | ARIA role automatically assigned |
hidden | boolean | Hides the panel when not active |
Best Practices
- Keep tab labels concise and descriptive.
- Use consistent styling to visually group tabs.
- Add transitions for panels to create a smooth user experience.
- Use
selectedIndex
andselectedIndexChange
for two-way binding when needed.