Skip to content

TabbedContent

Added in version 0.16.0

Switch between mutually exclusive content panes via a row of tabs.

  • Focusable
  • Container

This widget combines the Tabs and ContentSwitcher widgets to create a convenient way of navigating content.

Only a single child of TabbedContent is visible at once. Each child has an associated tab which will make it visible and hide the others.

Composing

There are two ways to provide the titles for the tab. You can pass them as positional arguments to the TabbedContent constructor:

def compose(self) -> ComposeResult:
    with TabbedContent("Leto", "Jessica", "Paul"):
        yield Markdown(LETO)
        yield Markdown(JESSICA)
        yield Markdown(PAUL)

Alternatively you can wrap the content in a TabPane widget, which takes the tab title as the first parameter:

def compose(self) -> ComposeResult:
    with TabbedContent():
        with TabPane("Leto"):
            yield Markdown(LETO)
        with TabPane("Jessica"):
            yield Markdown(JESSICA)
        with TabPane("Paul"):
            yield Markdown(PAUL)

Switching tabs

If you need to programmatically switch tabs, you should provide an id attribute to the TabPanes.

def compose(self) -> ComposeResult:
    with TabbedContent():
        with TabPane("Leto", id="leto"):
            yield Markdown(LETO)
        with TabPane("Jessica", id="jessica"):
            yield Markdown(JESSICA)
        with TabPane("Paul", id="paul"):
            yield Markdown(PAUL)

You can then switch tabs by setting the active reactive attribute:

# Switch to Jessica tab
self.query_one(TabbedContent).active = "jessica"

Note

If you don't provide id attributes to the tab panes, they will be assigned sequentially starting at tab-1 (then tab-2 etc).

Initial tab

The first child of TabbedContent will be the initial active tab by default. You can pick a different initial tab by setting the initial argument to the id of the tab:

def compose(self) -> ComposeResult:
    with TabbedContent(initial="jessica"):
        with TabPane("Leto", id="leto"):
            yield Markdown(LETO)
        with TabPane("Jessica", id="jessica"):
            yield Markdown(JESSICA)
        with TabPane("Paul", id="paul"):
            yield Markdown(PAUL)

Example

The following example contains a TabbedContent with three tabs.

TabbedApp LetoJessicaPaul ━━━━━━━━╸━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Lady Jessica   Bene Gesserit and concubine of Leto, and mother of Paul and Alia. PaulAlia ━╸━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ First child                                                               l Leto  j Jessica  p Paul 

from textual.app import App, ComposeResult
from textual.widgets import Footer, Label, Markdown, TabbedContent, TabPane

LETO = """
# Duke Leto I Atreides

Head of House Atreides.
"""

JESSICA = """
# Lady Jessica

Bene Gesserit and concubine of Leto, and mother of Paul and Alia.
"""

PAUL = """
# Paul Atreides

Son of Leto and Jessica.
"""


class TabbedApp(App):
    """An example of tabbed content."""

    BINDINGS = [
        ("l", "show_tab('leto')", "Leto"),
        ("j", "show_tab('jessica')", "Jessica"),
        ("p", "show_tab('paul')", "Paul"),
    ]

    def compose(self) -> ComposeResult:
        """Compose app with tabbed content."""
        # Footer to show keys
        yield Footer()

        # Add the TabbedContent widget
        with TabbedContent(initial="jessica"):
            with TabPane("Leto", id="leto"):  # First tab
                yield Markdown(LETO)  # Tab content
            with TabPane("Jessica", id="jessica"):
                yield Markdown(JESSICA)
                with TabbedContent("Paul", "Alia"):
                    yield TabPane("Paul", Label("First child"))
                    yield TabPane("Alia", Label("Second child"))

            with TabPane("Paul", id="paul"):
                yield Markdown(PAUL)

    def action_show_tab(self, tab: str) -> None:
        """Switch to a new tab."""
        self.get_child_by_type(TabbedContent).active = tab


if __name__ == "__main__":
    app = TabbedApp()
    app.run()

Styling

The TabbedContent widget is composed of two main sub-widgets: a Tabs and a ContentSwitcher; you can style them accordingly.

The tabs within the Tabs widget will have prefixed IDs; each ID being the ID of the TabPane the Tab is for, prefixed with --content-tab-. If you wish to style individual tabs within the TabbedContent widget you will need to use that prefix for the Tab IDs.

For example, to create a TabbedContent that has red and green labels:

ColorTabsApp RedGreen ━╸━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Red!                                                                        

from textual.app import App, ComposeResult
from textual.widgets import Label, TabbedContent, TabPane


class ColorTabsApp(App):
    CSS = """
    TabbedContent #--content-tab-green {
        color: green;
    }

    TabbedContent #--content-tab-red {
        color: red;
    }
    """

    def compose(self) -> ComposeResult:
        with TabbedContent():
            with TabPane("Red", id="red"):
                yield Label("Red!")
            with TabPane("Green", id="green"):
                yield Label("Green!")


if __name__ == "__main__":
    ColorTabsApp().run()

Reactive Attributes

Name Type Default Description
active str "" The id attribute of the active tab. Set this to switch tabs.

Messages

Bindings

This widget has no bindings.

Component Classes

This widget has no component classes.

See also


Bases: Widget

A container with associated tabs to toggle content visibility.

Parameters:

Name Type Description Default

*titles

TextType

Positional argument will be used as title.

()

initial

str

The id of the initial tab, or empty string to select the first tab.

''

name

str | None

The name of the button.

None

id

str | None

The ID of the button in the DOM.

None

classes

str | None

The CSS classes of the button.

None

disabled

bool

Whether the button is disabled or not.

False

active class-attribute instance-attribute

active = reactive('', init=False)

The ID of the active tab, or empty string if none are active.

active_pane property

active_pane

The currently active pane, or None if no pane is active.

tab_count property

tab_count

Total number of tabs.

Cleared

Cleared(tabbed_content)

Bases: Message

Posted when no tab pane is active.

This can happen if all tab panes are removed or if the currently active tab pane is unset.

Parameters:

Name Type Description Default

tabbed_content

TabbedContent

The TabbedContent widget.

required

control property

control

The TabbedContent widget that was cleared of all tab panes.

This is an alias for Cleared.tabbed_content and is used by the on decorator.

tabbed_content instance-attribute

tabbed_content = tabbed_content

The TabbedContent widget that contains the tab activated.

TabActivated

TabActivated(tabbed_content, tab)

Bases: Message

Posted when the active tab changes.

Parameters:

Name Type Description Default

tabbed_content

TabbedContent

The TabbedContent widget.

required

tab

ContentTab

The Tab widget that was selected (contains the tab label).

required

ALLOW_SELECTOR_MATCH class-attribute instance-attribute

ALLOW_SELECTOR_MATCH = {'pane'}

Additional message attributes that can be used with the on decorator.

control property

control

The TabbedContent widget that contains the tab activated.

This is an alias for TabActivated.tabbed_content and is used by the on decorator.

pane instance-attribute

pane = get_pane(tab)

The TabPane widget that was activated by selecting the tab.

tab instance-attribute

tab = tab

The Tab widget that was selected (contains the tab label).

tabbed_content instance-attribute

tabbed_content = tabbed_content

The TabbedContent widget that contains the tab activated.

add_pane

add_pane(pane, *, before=None, after=None)

Add a new pane to the tabbed content.

Parameters:

Name Type Description Default

pane

TabPane

The pane to add.

required

before

TabPane | str | None

Optional pane or pane ID to add the pane before.

None

after

TabPane | str | None

Optional pane or pane ID to add the pane after.

None

Returns:

Type Description
AwaitComplete

An optionally awaitable object that waits for the pane to be added.

Raises:

Type Description
TabError

If there is a problem with the addition request.

Note

Only one of before or after can be provided. If both are provided an exception is raised.

clear_panes

clear_panes()

Remove all the panes in the tabbed content.

Returns:

Type Description
AwaitComplete

An optionally awaitable object which waits for all panes to be removed and the Cleared message to be posted.

disable_tab

disable_tab(tab_id)

Disables the tab with the given ID.

Parameters:

Name Type Description Default

tab_id

str

The ID of the TabPane to disable.

required

Raises:

Type Description
TabError

If there are any issues with the request.

enable_tab

enable_tab(tab_id)

Enables the tab with the given ID.

Parameters:

Name Type Description Default

tab_id

str

The ID of the TabPane to enable.

required

Raises:

Type Description
TabError

If there are any issues with the request.

get_pane

get_pane(pane_id)

Get the TabPane associated with the given ID or tab.

Parameters:

Name Type Description Default

pane_id

str | ContentTab

The ID of the pane to get, or the Tab it is associated with.

required

Returns:

Type Description
TabPane

The TabPane associated with the ID or the given tab.

Raises:

Type Description
ValueError

Raised if no ID was available.

get_tab

get_tab(pane_id)

Get the Tab associated with the given ID or TabPane.

Parameters:

Name Type Description Default

pane_id

str | TabPane

The ID of the pane, or the pane itself.

required

Returns:

Type Description
Tab

The Tab associated with the ID.

Raises:

Type Description
ValueError

Raised if no ID was available.

hide_tab

hide_tab(tab_id)

Hides the tab with the given ID.

Parameters:

Name Type Description Default

tab_id

str

The ID of the TabPane to hide.

required

Raises:

Type Description
TabError

If there are any issues with the request.

remove_pane

remove_pane(pane_id)

Remove a given pane from the tabbed content.

Parameters:

Name Type Description Default

pane_id

str

The ID of the pane to remove.

required

Returns:

Type Description
AwaitComplete

An optionally awaitable object that waits for the pane to be removed and the Cleared message to be posted.

show_tab

show_tab(tab_id)

Shows the tab with the given ID.

Parameters:

Name Type Description Default

tab_id

str

The ID of the TabPane to show.

required

Raises:

Type Description
TabError

If there are any issues with the request.


Bases: Widget

A container for switchable content, with additional title.

This widget is intended to be used with TabbedContent.

Parameters:

Name Type Description Default

title

TextType

Title of the TabPane (will be displayed in a tab label).

required

*children

Widget

Widget to go inside the TabPane.

()

name

str | None

Optional name for the TabPane.

None

id

str | None

Optional ID for the TabPane.

None

classes

str | None

Optional initial classes for the widget.

None

disabled

bool

Whether the TabPane is disabled or not.

False

Disabled dataclass

Disabled(tab_pane)

Bases: TabPaneMessage

Sent when a tab pane is disabled via its reactive disabled.

Enabled dataclass

Enabled(tab_pane)

Bases: TabPaneMessage

Sent when a tab pane is enabled via its reactive disabled.

Focused dataclass

Focused(tab_pane)

Bases: TabPaneMessage

Sent when a child widget is focused.

TabPaneMessage dataclass

TabPaneMessage(tab_pane)

Bases: Message

Base class for TabPane messages.

control property

control

The tab pane that is the object of this message.

This is an alias for the attribute tab_pane and is used by the on decorator.

tab_pane instance-attribute

tab_pane

The TabPane that is he object of this message.