Render Bootstrap 5 nav tabs or pills with built-in JavaScript tab-switching behavior. Supports both in-page tab panels (buttons) and navigational links. Icons are rendered by IconHelper. Array URLs are built with CakePHP’s Url helper.
// Simple tabs with content panels
echo $this->Nav
->add('home', 'Home', '<p>Home content goes here...</p>')
->add('profile', 'Profile', '<p>Profile content goes here...</p>')
->add('settings', 'Settings', '<p>Settings content goes here...</p>')
->render();echo $this->Nav
->add('tab1', 'Tab 1', 'Content 1')
->add('tab2', 'Tab 2', 'Content 2')
->render(['type' => 'pills']);The first tab is active by default. Set 'active' => true on one tab to open a different panel; only that tab and its pane receive the active state.
echo $this->Nav
->add('overview', 'Overview', $overviewContent)
->add('details', 'Details', $detailsContent, ['active' => true])
->render();Icons use the IconHelper defaults (Bootstrap Icons unless you configure another set). namespace, prefix, and size on one item override that set and are not copied onto the button or link.
echo $this->Nav
->add('home', 'Home', 'Home content', ['icon' => 'house'])
->add('profile', 'Profile', 'Profile content', ['icon' => 'person'])
->add('settings', 'Settings', 'Settings content', ['icon' => 'gear'])
->add('code', 'Code', 'Code content', [
'icon' => 'github',
'namespace' => 'fa-brands',
'prefix' => 'fa',
])
->render();For navigation between pages (no tab panels):
echo $this->Nav
->addLink('Dashboard', ['controller' => 'Dashboard', 'action' => 'index'], ['active' => true])
->addLink('Users', ['controller' => 'Users', 'action' => 'index'])
->addLink('Settings', ['controller' => 'Settings', 'action' => 'index'], ['icon' => 'gear'])
->renderNav(['type' => 'pills']);echo $this->Nav
->add('details', 'Details', $detailsContent)
->add('history', 'History', $historyContent)
->addLink('View All', ['action' => 'index'], ['icon' => 'list'])
->render();| Method | Description |
|---|---|
add($id, $title, $content, $options) |
Add a tab with panel content (renders as button) |
addLink($title, $url, $options) |
Add a navigational link (no panel) |
render($options) |
Render nav with tab content panels |
renderNav($options) |
Render nav only (no panels) |
| Option | Type | Default | Description |
|---|---|---|---|
icon |
string | null |
Icon name rendered by IconHelper (e.g., 'house', 'gear') |
namespace |
string | IconHelper default | Icon set class for this item (e.g., 'fa-brands', 'ti') |
prefix |
string | IconHelper default | Icon class prefix for this item (e.g., 'fa', 'ti') |
size |
string | null |
Icon size token for this item (e.g., 'lg') |
active |
bool | false |
Mark tab/link as active (add: first tab is active if none set; only one tab panel is active) |
disabled |
bool | false |
Disable the tab/link |
| Other options | mixed | — | HTML attributes for the tab button (add) or link (addLink) |
| Option | Type | Default | Description |
|---|---|---|---|
type |
string | 'tabs' |
Nav type: 'tabs' or 'pills' |
fade |
bool | true |
Enable fade animation on tab switch |
fill |
bool | false |
Make nav items fill available width |
justified |
bool | false |
Make nav items equal width |
vertical |
bool | false |
Render nav vertically with flex layout |
navAttrs |
array | [] |
HTML attributes for the nav element |
contentAttrs |
array | [] |
HTML attributes for the tab-content wrapper |
The NavHelper uses the following default templates:
| Template | Default HTML |
|---|---|
nav |
<ul{{attrs}}>{{content}}</ul> |
navItem |
<li{{attrs}}>{{content}}</li> |
navButton |
<button{{attrs}}>{{content}}</button> |
navLink |
<a{{attrs}}>{{content}}</a> |
tabContent |
<div{{attrs}}>{{content}}</div> |
tabPane |
<div{{attrs}}>{{content}}</div> |
| Element | Default Classes |
|---|---|
| nav | nav nav-tabs or nav nav-pills |
| navItem | nav-item |
| navButton | nav-link |
| navLink | nav-link |
| tabContent | tab-content |
| tabPane | tab-pane fade |
The helper automatically includes proper ARIA attributes:
- Nav:
role="tablist" - Nav item:
role="presentation"(tab buttons only; omitted foraddLinkitems) - Tab button:
id="{tab-id}-tab",role="tab",aria-controls,aria-selected - Tab pane:
id="{tab-id}",role="tabpanel",tabindex="0",aria-labelledby="{tab-id}-tab" - Disabled:
aria-disabled="true",tabindex="-1" - Active link:
aria-current="page"
Tab switching is handled automatically by Bootstrap 5's JavaScript via data attributes:
data-bs-toggle="tab"on buttonsdata-bs-target="#panel-id"pointing to the tab pane
No additional JavaScript is required.
// Define tab content
$personalInfo = $this->element('user/personal_info', ['user' => $user]);
$security = $this->element('user/security', ['user' => $user]);
$preferences = $this->element('user/preferences', ['user' => $user]);
// Render tabs
echo $this->Nav
->add('personal', 'Personal Info', $personalInfo, ['icon' => 'person'])
->add('security', 'Security', $security, ['icon' => 'shield-lock'])
->add('preferences', 'Preferences', $preferences, ['icon' => 'sliders'])
->render(['type' => 'pills', 'fill' => true]);With tab panels, vertical layout wraps the nav and content in <div class="d-flex align-items-start"> and adds flex-column to the nav.
echo $this->Nav
->add('overview', 'Overview', $overviewContent)
->add('analytics', 'Analytics', $analyticsContent)
->add('reports', 'Reports', $reportsContent)
->add('settings', 'Settings', $settingsContent, ['icon' => 'gear'])
->render(['type' => 'pills', 'vertical' => true]);echo $this->Nav
->addLink('All Users', ['action' => 'index'], ['active' => $this->request->getParam('action') === 'index'])
->addLink('Active', ['action' => 'index', '?' => ['status' => 'active']])
->addLink('Inactive', ['action' => 'index', '?' => ['status' => 'inactive']])
->addLink('Add New', ['action' => 'add'], ['icon' => 'plus'])
->renderNav(['type' => 'pills']);See also: Template customization