HyTabPanel
Usage examples on this page are written for Lit / plain HTML (
<hy-tab-panel>). The same component ships asHyTabPanelin@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
A tab panel component that displays content associated with a specific tab. Designed to work within a tab group system and supports lazy loading for performance optimization. Provides proper accessibility attributes and visibility management based on tab selection state.
Usage Guidelines
Tab panels should be used within an hy-tab-group component. Each panel is associated with a specific tab via matching name attributes. Only the active panel is visible at any time.
Lazy Loading
When the lazy attribute is set, panel content is only rendered after the panel has been activated at least once. This improves initial page load performance for tabs with heavy content.
Note: Lazy loading intentionally triggers a Lit "scheduled an update after an update completed" warning when a panel is first activated. This is expected behavior - the warning cannot be avoided without breaking SSR compatibility. The second render cycle is required to display content after the hasBeenActive state is set.
Accessibility
- Uses
role="tabpanel"for proper ARIA semantics - Sets
aria-hiddenbased on active state - Manages
tabindexfor keyboard navigation - Supports focus management for keyboard users
Examples
Basic usage
<hy-tab-panel name="panel1">
<h2>Panel Content</h2>
<p>This content is displayed when the associated tab is selected.</p>
</hy-tab-panel>Within tab group
<hy-tab-group>
<hy-tab slot="tab" label="Overview" panel="overview-panel"></hy-tab>
<hy-tab slot="tab" label="Details" panel="details-panel"></hy-tab>
<hy-tab-panel slot="panel" name="overview-panel">
<div class="overview-content">
<h3>Project Overview</h3>
<p>Summary of key project metrics and status.</p>
</div>
</hy-tab-panel>
<hy-tab-panel slot="panel" name="details-panel">
<div class="details-content">
<h3>Detailed Information</h3>
<p>Comprehensive project details and specifications.</p>
</div>
</hy-tab-panel>
</hy-tab-group>Lazy loading for performance
<hy-tab-panel name="heavy-content" lazy>
<div class="expensive-content">
<h3>Resource-Intensive Content</h3>
<div class="chart-container">
<!-- Complex charts or heavy DOM content -->
<canvas id="performance-chart"></canvas>
</div>
<div class="data-table">
<!-- Large data tables -->
<table>
<!-- Thousands of rows -->
</table>
</div>
</div>
</hy-tab-panel>Vertical orientation
<hy-tab-group orientation="vertical-left">
<hy-tab slot="tab" label="Dashboard" panel="dashboard"></hy-tab>
<hy-tab slot="tab" label="Settings" panel="settings"></hy-tab>
<hy-tab-panel slot="panel" name="dashboard" orientation="vertical-left">
<div class="dashboard-layout">
<div class="widget-grid">
<div class="widget">
<h4>Active Users</h4>
<div class="metric">1,234</div>
</div>
<div class="widget">
<h4>Revenue</h4>
<div class="metric">$45,678</div>
</div>
</div>
</div>
</hy-tab-panel>
<hy-tab-panel slot="panel" name="settings" orientation="vertical-left">
<div class="settings-form">
<h4>Application Settings</h4>
<form>
<label
>Theme:
<select>
<option>Light</option>
<option>Dark</option>
</select></label
>
<label
>Language:
<select>
<option>English</option>
<option>Spanish</option>
</select></label
>
</form>
</div>
</hy-tab-panel>
</hy-tab-group>Form-based panels
<hy-tab-group>
<hy-tab slot="tab" label="Personal Info" panel="personal"></hy-tab>
<hy-tab slot="tab" label="Contact Details" panel="contact"></hy-tab>
<hy-tab slot="tab" label="Preferences" panel="preferences"></hy-tab>
<hy-tab-panel slot="panel" name="personal">
<form class="personal-form">
<h3>Personal Information</h3>
<div class="form-row">
<label>First Name: <input type="text" required /></label>
<label>Last Name: <input type="text" required /></label>
</div>
<div class="form-row">
<label>Date of Birth: <input type="date" /></label>
<label
>Gender:
<select>
<option>Prefer not to say</option>
</select></label
>
</div>
</form>
</hy-tab-panel>
<hy-tab-panel slot="panel" name="contact">
<form class="contact-form">
<h3>Contact Details</h3>
<div class="form-row">
<label>Email: <input type="email" required /></label>
<label>Phone: <input type="tel" /></label>
</div>
<div class="form-row">
<label>Address: <textarea rows="3"></textarea></label>
</div>
</form>
</hy-tab-panel>
<hy-tab-panel slot="panel" name="preferences" lazy>
<form class="preferences-form">
<h3>User Preferences</h3>
<div class="preference-group">
<h4>Notifications</h4>
<label><input type="checkbox" /> Email notifications</label>
<label><input type="checkbox" /> SMS notifications</label>
<label><input type="checkbox" /> Push notifications</label>
</div>
<div class="preference-group">
<h4>Privacy</h4>
<label><input type="radio" name="profile" /> Public profile</label>
<label><input type="radio" name="profile" /> Private profile</label>
</div>
</form>
</hy-tab-panel>
</hy-tab-group>Dynamic content with lazy loading
<hy-tab-panel name="analytics" lazy>
<div class="analytics-dashboard">
<h3>Analytics Dashboard</h3>
<div id="analytics-content">
<!-- Content loaded dynamically when panel first becomes active -->
</div>
</div>
<script>
// This code only runs when panel is first activated
fetch('/api/analytics')
.then((response) => response.json())
.then((data) => {
document.getElementById('analytics-content').innerHTML = renderAnalytics(data);
});
</script>
</hy-tab-panel>Programmatic Access
const panel = document.querySelector('hy-tab-panel[name="settings"]');
// Check if panel is active
console.log(panel.active);
// Check if panel uses lazy loading
console.log(panel.lazy);
// Get panel name
console.log(panel.name);Header toolbar
The toolbar slot hosts controls scoped to this panel (e.g. a Find + view-switch, or an Edit → Save/Cancel inline-edit set). It renders as the panel's first row, right-aligned, before the content in DOM order so it's announced first. The heading stays consumer-authored in the default slot — compose a Polaris-style header by placing the heading at the top of the content and letting the toolbar float to the trailing edge.
The toolbar auto-promotes to role="toolbar" when it holds 2 or more controls; name it with toolbar-label. The toolbar wrapper renders only when the slot is
### Inline-edit toolbar
```html
<hy-tab-panel slot="panel" name="general">
<hy-button slot="toolbar" emphasis="plain">Cancel</hy-button>
<hy-button slot="toolbar" variant="brand">Save</hy-button>
<h2>General</h2>
…
</hy-tab-panel>API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
name | name | string | '' | The unique identifier for this tab panel, used to match with tab's panel attribute. |
active | active | boolean | false | Marks the tab panel as active/visible. Typically managed by parent tab-group. |
orientation | orientation | 'horizontal' | 'vertical-left' | 'vertical-right' | 'horizontal' | The orientation of the tab panel (inherited from tab-group). |
lazy | lazy | boolean | false | Whether this panel is lazy-loaded (content only renders when first activated). Improves initial page load performance for tabs with heavy content. |
toolbarLabel | toolbar-label | string | '' | Accessible name applied as aria-label to the toolbar when it auto-promotes to role="toolbar" (2+ controls). Recommended whenever the toolbar carries multiple controls so the toolbar has a name. |
label | label | string | '' | Accessible name for the tabpanel, announced by screen readers. When omitted, the parent hy-tab-group syncs it from the matching tab's label. Forwarded as a string because a plain IDREF aria-labelledby cannot reach role="tab" in the sibling tab's shadow root. |
Slots
| Slot | Description |
|---|---|
default | The content to display when the tab panel is active |
toolbar | Panel-scoped controls rendered as a right-aligned header row above the content. Auto-promotes to role="toolbar" with 2+ controls. |
CSS Parts
| Part | Description |
|---|---|
base | The tab panel's base wrapper element |
toolbar | The panel header toolbar region |
CSS Custom Properties
| Property | Description |
|---|---|
--hy-tab-panel-toolbar-gap | Gap between toolbar controls and below the toolbar. Default: var(--hy-gap-layout-sm). |