Skip to content

TabPanel ​

A tabbed interface for organizing content into switchable panels. TabPanel provides a complete solution with a tab bar, content area, and optional navigation controls.

Basic Usage ​

Create a tabbed interface using the fluent API. By default, TabPanel manages its own selection state (the first tab is selected initially):

csharp
using Hex1b;

await using var terminal = Hex1bTerminal.CreateBuilder()
    .WithHex1bApp((app, options) => ctx => ctx.TabPanel(tp => [
        tp.Tab("Overview", t => [
            t.Text("Welcome to Hex1b!"),
            t.Text(""),
            t.Text("This is the Overview tab content.")
        ]),
        tp.Tab("Settings", t => [
            t.Text("Application Settings"),
            t.Text(""),
            t.Text("Configure your preferences here.")
        ]),
        tp.Tab("Help", t => [
            t.Text("Documentation and Support"),
            t.Text(""),
            t.Text("Visit hex1b.dev for more information.")
        ])
    ]).Selector().Fill())
    .Build();

await terminal.RunAsync();

Selection with State ​

For controlled selection, use .Selected(condition) to declaratively set which tab is active based on your application state:

csharp
using Hex1b;

var state = new TabState();

await using var terminal = Hex1bTerminal.CreateBuilder()
    .WithHex1bApp((app, options) => ctx => ctx.VStack(v => [
        v.Text($"Current tab: {state.SelectedTab}"),
        v.Text(""),
        v.TabPanel(tp => [
            tp.Tab("Documents", t => [
                t.Text("Your documents appear here")
            ]).Selected(state.SelectedTab == "Documents"),
            tp.Tab("Downloads", t => [
                t.Text("Your downloads appear here")
            ]).Selected(state.SelectedTab == "Downloads"),
            tp.Tab("Pictures", t => [
                t.Text("Your pictures appear here")
            ]).Selected(state.SelectedTab == "Pictures")
        ])
        .OnSelectionChanged(e => state.SelectedTab = e.SelectedTitle)
        .Selector()
        .Fill()
    ]))
    .Build();

await terminal.RunAsync();

class TabState
{
    public string SelectedTab { get; set; } = "Documents";
}

Key points:

  • Without .Selected(), TabPanel manages its own state (uncontrolled)
  • .Selected(condition) enables controlled selection based on state
  • When multiple tabs have .Selected(true), the first one wins
  • Use OnSelectionChanged to respond to user tab switches

Dynamic Tabs ​

Build tab-based interfaces where tabs are added and removed at runtime. This pattern is common for document editors, browser-like UIs, and multi-pane dashboards:

csharp
using Hex1b;

var state = new EditorState();

await using var terminal = Hex1bTerminal.CreateBuilder()
    .WithMouse()
    .WithHex1bApp((app, options) => ctx => ctx.VStack(v => [
        v.HStack(h => [
            h.Button("New Tab").OnClick(_ => state.AddTab()),
            h.Text($"  {state.Tabs.Count} tab(s) open")
        ]),
        v.Text(""),
        state.Tabs.Count == 0
            ? v.Text("No tabs open. Click 'New Tab' to add one.")
            : v.TabPanel(tp => state.Tabs.Select((tab, idx) =>
                tp.Tab(tab.Name, t => [
                    t.Text($"Content of {tab.Name}"),
                    t.Text(""),
                    t.Text($"Created at: {tab.CreatedAt:HH:mm:ss}")
                ])
                .Selected(idx == state.SelectedIndex)
                .RightActions(i => [
                    i.Icon("×").OnClick(_ => state.CloseTab(idx))
                ])
            ).ToArray())
            .OnSelectionChanged(e => state.SelectedIndex = e.SelectedIndex)
            .Selector()
            .Fill()
    ]))
    .Build();

await terminal.RunAsync();

class EditorState
{
    public List<TabInfo> Tabs { get; } = [];
    public int SelectedIndex { get; set; }
    private int _counter = 1;

    public void AddTab()
    {
        Tabs.Add(new TabInfo($"Tab {_counter++}", DateTime.Now));
        SelectedIndex = Tabs.Count - 1;
    }

    public void CloseTab(int index)
    {
        if (index >= 0 && index < Tabs.Count)
        {
            Tabs.RemoveAt(index);
            if (SelectedIndex >= Tabs.Count)
                SelectedIndex = Math.Max(0, Tabs.Count - 1);
        }
    }
}

record TabInfo(string Name, DateTime CreatedAt);

Features demonstrated:

  • Adding tabs dynamically with state
  • Close button using .RightActions()
  • Selection tracking with OnSelectionChanged
  • Conditional rendering when no tabs exist

Render Modes ​

TabPanel supports two visual styles:

Full Mode (Default) ​

Full mode displays visual separators above and below the tabs:

csharp
tp.TabPanel(...).Full()

Compact Mode ​

Compact mode shows only the tab row without separators, saving vertical space:

csharp

Tab Bar Features ​

Paging Arrows ​

When tabs overflow the available width, paging arrows (◀ ▶) appear automatically. Disable with:

csharp
tp.TabPanel(...).Paging(false)

Enable a dropdown menu (▼) for quick navigation to any tab:

csharp
tp.TabPanel(...).Selector()

Mouse Wheel Scrolling ​

When the mouse is over the tab bar, use the scroll wheel to page through tabs without changing the selection.

Tab Position ​

Tabs can appear at the top or bottom of the panel:

csharp
// Explicit positioning
tp.TabPanel(...).TabsOnTop()
tp.TabPanel(...).TabsOnBottom()

By default, TabPanel auto-detects position based on its location in a VStack:

  • First child → tabs on top
  • Last child → tabs on bottom
  • Otherwise → tabs on top

Tab Icons ​

Add icons to tabs for visual identification:

csharp
tp.Tab("Settings", t => [...]).Icon("⚙️")

Tab Actions ​

Add interactive action icons to tabs using .LeftActions() and .RightActions():

Close Button (Right Action) ​

csharp
tp.Tab("Document.cs", t => [...])
  .RightActions(a => [
      a.Icon("×").OnClick(_ => CloseDocument())
  ])

Pin Button (Left Action) ​

csharp
tp.Tab("Important.cs", t => [...])
  .LeftActions(a => [
      a.Icon("📌").OnClick(_ => TogglePin())
  ])

Multiple Actions ​

csharp
tp.Tab("Document.cs", t => [...])
  .LeftActions(a => [
      a.Icon("📌").OnClick(_ => TogglePin())
  ])
  .RightActions(a => [
      a.Icon("💾").OnClick(_ => Save()),
      a.Icon("×").OnClick(_ => Close())
  ])

Keyboard Navigation ​

KeyAction
Alt+RightSwitch to next tab
Alt+LeftSwitch to previous tab
TabMove focus to next focusable element
Shift+TabMove focus to previous focusable element
  • Navigator - Stack-based page navigation
  • Splitter - Resizable split views (often used with TabPanel)
  • Scroll - Scrollable content within tabs
  • List - Selectable item lists

Released under the MIT License.