Tabs

Links that swap a panel loaded from the server.

Each tab is a real link to a page URL that shows that tab. With HTMX the panel loads from {href}:{block} and the URL is pushed, and without JavaScript the link is a plain navigation. The server decides which tab is active, so the page wraps the component in a {fragment} that swaps the whole thing.

{fragment 'tabs', swap: 'outerHTML'}
	{component tabs, items: $items}
		{$panel}
	{/component}
{/fragment}

An item is ['title' => 'Plan', 'href' => '/account/plan', 'active' => true]. A tab whose URL has a query string adds hx, the fragment URL, because the block name goes before the query: /account:tabs?tab=plan. An item can also be a page, which becomes its title, URL and active state. This page does that below, and swaps its own docs fragment.

Server-loaded panels
Pick a tab to load its panel from the server.
{var $url = $page->url()}
{var $tab = $request->query('tab') ?? 'overview'}

{component tabs,
	block: 'docs',
	id: 'tabs-demo',
	items: [
		[
			'title' => 'Overview',
			'href' => "{$url}?tab=overview",
			'hx' => "{$url}:docs?tab=overview",
			'active' => $tab === 'overview'
		],
		[
			'title' => 'Usage',
			'href' => "{$url}?tab=usage",
			'hx' => "{$url}:docs?tab=usage",
			'active' => $tab === 'usage'
		],
	],
}
	{if $tab === 'usage'}
		Each tab is a link, so the panel also loads without JavaScript.
	{else}
		Pick a tab to load its panel from the server.
	{/if}
{/component}

Params

Param Default Description
items [] the tabs: pages or arrays like ['title' => 'Plan', 'href' => '/account/plan', 'active' => true]; an array's hx overrides the fragment URL
block 'tabs' the page's {fragment} the tabs load
id null the root's id, the block name when null, so a fragment swapping outerHTML can target it
body null the active panel as a string, or the tag's content
class '' added to tabs
attrs [] any other attributes, e.g. ['data-x' => 'y']