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
#templateRefsand 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-selectedon each tabrole="tabpanel"andhiddenon each panel- Keyboard navigation:
←,→,Home,Endto 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
selectedIndexandselectedIndexChangefor two-way binding when needed.