Skip to content


Added in version 0.14.0

A widget for containing and switching display between multiple child widgets.

  • Focusable
  • Container


The example below uses a ContentSwitcher in combination with two Buttons to create a simple tabbed view. Note how each Button has an ID set, and how each child of the ContentSwitcher has a corresponding ID; then a Button.Clicked handler is used to set ContentSwitcher.current to switch between the different views.

ContentSwitcherApp ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔  DataTable  Markdown  ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ────────────────────────────────────────────────────────────────────  Book                                 Year   Dune                                 1965   Dune Messiah                         1969   Children of Dune                     1976   God Emperor of Dune                  1981   Heretics of Dune                     1984   Chapterhouse: Dune                   1985  ────────────────────────────────────────────────────────────────────

from rich.align import VerticalCenter

from import App, ComposeResult
from textual.containers import Horizontal, VerticalScroll
from textual.widgets import Button, ContentSwitcher, DataTable, Markdown

MARKDOWN_EXAMPLE = """# Three Flavours Cornetto

The Three Flavours Cornetto trilogy is an anthology series of British
comedic genre films directed by Edgar Wright.

## Shaun of the Dead

| Flavour | UK Release Date | Director |
| -- | -- | -- |
| Strawberry | 2004-04-09 | Edgar Wright |

## Hot Fuzz

| Flavour | UK Release Date | Director |
| -- | -- | -- |
| Classico | 2007-02-17 | Edgar Wright |

## The World's End

| Flavour | UK Release Date | Director |
| -- | -- | -- |
| Mint | 2013-07-19 | Edgar Wright |

class ContentSwitcherApp(App[None]):
    CSS_PATH = "content_switcher.tcss"

    def compose(self) -> ComposeResult:
        with Horizontal(id="buttons"):  # (1)!
            yield Button("DataTable", id="data-table")  # (2)!
            yield Button("Markdown", id="markdown")  # (3)!

        with ContentSwitcher(initial="data-table"):  # (4)!
            yield DataTable(id="data-table")
            with VerticalScroll(id="markdown"):
                yield Markdown(MARKDOWN_EXAMPLE)

    def on_button_pressed(self, event: Button.Pressed) -> None:
        self.query_one(ContentSwitcher).current =  # (5)!

    def on_mount(self) -> None:
        table = self.query_one(DataTable)
        table.add_columns("Book", "Year")
                (title.ljust(35), year)
                for title, year in (
                    ("Dune", 1965),
                    ("Dune Messiah", 1969),
                    ("Children of Dune", 1976),
                    ("God Emperor of Dune", 1981),
                    ("Heretics of Dune", 1984),
                    ("Chapterhouse: Dune", 1985),

if __name__ == "__main__":
  1. A Horizontal to hold the buttons, each with a unique ID.
  2. This button will select the DataTable in the ContentSwitcher.
  3. This button will select the Markdown in the ContentSwitcher.
  4. Note that the initial visible content is set by its ID, see below.
  5. When a button is pressed, its ID is used to switch to a different widget in the ContentSwitcher. Remember that IDs are unique within parent, so the buttons and the widgets in the ContentSwitcher can share IDs.
Screen {
    align: center middle;
    padding: 1;

#buttons {
    height: 3;
    width: auto;

ContentSwitcher {
    background: $panel;
    border: round $primary;
    width: 90%;
    height: 1fr;

DataTable {
    background: $panel;

MarkdownH2 {
    background: $primary;
    color: yellow;
    border: none;
    padding: 0;

When the user presses the "Markdown" button the view is switched:

ContentSwitcherApp ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔  DataTable  Markdown  ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ──────────────────────────────────────────────────────────────────── ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ Three Flavours Cornetto ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ The Three Flavours Cornetto trilogy is an anthology series of British comedic genre films directed by Edgar Wright.                     Shaun of the Dead                      ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ Flavour        UK Release Date      Director          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  Strawberry     2004-04-09           Edgar Wright     ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔                          Hot Fuzz                          ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ Flavour     UK Release Date        Director           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  Classico    2007-02-17             Edgar Wright      ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔                      The World's End                       ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ Flavour    UK Release Date        Director           ────────────────────────────────────────────────────────────────────

Reactive Attributes

Name Type Default Description
current str | None None The ID of the currently-visible child. None means nothing is visible.


This widget posts no messages.


This widget has no bindings.

Component Classes

This widget has no component classes.

textual.widgets.ContentSwitcher class

def __init__(

Bases: Container

A widget for switching between different children.


All child widgets that are to be switched between need a unique ID. Children that have no ID will be hidden and ignored.

Name Type Description Default
*children Widget

The widgets to switch between.

name str | None

The name of the content switcher.

id str | None

The ID of the content switcher in the DOM.

classes str | None

The CSS classes of the content switcher.

disabled bool

Whether the content switcher is disabled or not.

initial str | None

The ID of the initial widget to show, None or empty string for the first tab.


If initial is not supplied no children will be shown to start with.

current class-attribute instance-attribute

current: reactive[str | None] = reactive[Optional[str]](
    None, init=False

The ID of the currently-displayed widget.

If set to None then no widget is visible.


If set to an unknown ID, this will result in NoMatches being raised.

visible_content property

visible_content: Widget | None

A reference to the currently-visible widget.

None if nothing is visible.

watch_current method

def watch_current(self, old, new):

React to the current visible child choice being changed.

Name Type Description Default
old str | None

The old widget ID (or None if there was no widget).

new str | None

The new widget ID (or None if nothing should be shown).