Skip to content

Widgets

In this chapter we will explore widgets in more detail, and how you can create custom widgets of your own.

What is a widget?

A widget is a component of your UI responsible for managing a rectangular region of the screen. Widgets may respond to events in much the same way as an app. In many respects, widgets are like mini-apps.

Information

Every widget runs in its own asyncio task.

Custom widgets

There is a growing collection of builtin widgets in Textual, but you can build entirely custom widgets that work in the same way.

The first step in building a widget is to import and extend a widget class. This can either be Widget which is the base class of all widgets, or one of its subclasses.

Let's create a simple custom widget to display a greeting.

hello01.py
from textual.app import App, ComposeResult, RenderResult
from textual.widget import Widget


class Hello(Widget):
    """Display a greeting."""

    def render(self) -> RenderResult:
        return "Hello, [b]World[/b]!"


class CustomApp(App):
    def compose(self) -> ComposeResult:
        yield Hello()


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

The highlighted lines define a custom widget class with just a render() method. Textual will display whatever is returned from render in the content area of your widget.

Note that the text contains tags in square brackets, i.e. [b]. This is content markup which allows you to embed various styles within your content. If you run this you will find that World is in bold.

CustomApp Hello, World!

This (very simple) custom widget may be styled in the same way as builtin widgets, and targeted with CSS. Let's add some CSS to this app.

hello02.py
from textual.app import App, ComposeResult, RenderResult
from textual.widget import Widget


class Hello(Widget):
    """Display a greeting."""

    def render(self) -> RenderResult:
        return "Hello, [b]World[/b]!"


class CustomApp(App):
    CSS_PATH = "hello02.tcss"

    def compose(self) -> ComposeResult:
        yield Hello()


if __name__ == "__main__":
    app = CustomApp()
    app.run()
hello02.tcss
Screen {
    align: center middle;
}

Hello {
    width: 40;
    height: 9;
    padding: 1 2;
    background: $panel;
    color: $text;
    border: $secondary tall;
    content-align: center middle;
}

The addition of the CSS has completely transformed our custom widget.

CustomApp ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ Hello, World! ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

Static widget

While you can extend the Widget class, a subclass will typically be a better starting point. The Static class is a widget subclass which caches the result of render, and provides an update() method to update the content area.

Let's use Static to create a widget which cycles through "hello" in various languages.

hello03.py
from itertools import cycle

from textual.app import App, ComposeResult
from textual.widgets import Static

hellos = cycle(