cloudparts

Textual

A Python framework for terminal apps with a widget tree, CSS-style stylesheets and asyncio event handling, built on the Rich library; the same app can also be served to a web browser.

Type
Open source
Made by
Will McGugan and contributors (built at Textualize, Inc. until mid-2025)
License
MIT
Facts checked
13 October 2026

What it is

Textual is retained mode, closer to a web page than a drawing API. An app subclasses App and yields widgets from compose(); they form a DOM-like tree under a Screen. Styles live in Textual CSS (.tcss files or a CSS string) with type, id and class selectors and rules such as dock: top; unlike browser CSS, a type selector also matches subclasses. Events and messages are handled by methods named on_<event>, and each widget is powered by its own task on Python’s asyncio, with input arriving as events on the app’s message queue. Reactive attributes refresh a widget automatically when they change, batching several changes into one repaint, and the Worker API moves slow work such as network calls off the message loop so the UI stays responsive.

Under the hood, widgets render to Rich segments (runs of text with one style). A compositor cuts the segments of overlapping widgets into equal-width “chops”, keeps the top-most ones and writes a single stream of escape codes, updating only the regions that changed. A spatial map on a grid of 100 by 20 cells lets it skip widgets scrolled out of view, so finding the visible ones takes about as long for 1,000 widgets as for 8. Apps can take over the screen or run inline below the prompt, and textual-serve runs an app on a server and streams it over a WebSocket to xterm.js in a browser.

Will McGugan, the author of Rich, started Textual in 2021 and founded Textualize, Inc. to work on it full time. In May 2025 the company announced it would wrap up because it hadn’t found a viable business; Textual continues as an MIT-licensed open-source project. It is still maintained, almost entirely by McGugan: version 8.2.8 shipped in June 2026 and commits landed in October 2026.

Why teams pick it

  • Web-style development in Python: a widget tree, CSS that restyles built-in and third-party widgets, and live reloading of styles with textual run my_app.py --dev
  • Ready-made widgets, from a basic button to a syntax-highlighted TextArea, with keyboard and mouse input handled for you
  • Concurrency fits Python’s asyncio: handlers can be async, and workers keep slow I/O from freezing the interface
  • One codebase for terminal and browser: through textual-serve, App.open_url and App.deliver_text work in both

Watch out for

  • Its company is gone: since Textualize closed in mid-2025, development depends mostly on one maintainer’s time
  • Frequent major versions: 1.0 arrived in December 2024 and 8.0 in February 2026, so upgrades need care
  • Needs Python 3.9 or later (3.10 for the tree-sitter syntax highlighting extras), and a terminal with more than 256 colors looks much better; macOS’s default Terminal is limited to 256
  • In the browser the app still runs on the server as a subprocess of textual-serve, with the terminal drawn by xterm.js: it is not a static web app

Alternatives

What all terminal user interfaces (tuis) have in common →Compare side by side →

How it fits with other technologies

A click counter with a header, footer, a key binding and inline CSS · python

from textual.app import App, ComposeResult
from textual.widgets import Button, Footer, Header, Static


class CounterApp(App):
    CSS = """
    Button { margin: 1 2; }
    #label { padding: 1 2; }
    """
    BINDINGS = [("q", "quit", "Quit")]
    clicks = 0

    def compose(self) -> ComposeResult:
        yield Header()
        yield Static("Clicked 0 times", id="label")
        yield Button("Click me", variant="primary")
        yield Footer()

    def on_button_pressed(self, event: Button.Pressed) -> None:
        self.clicks += 1
        self.query_one("#label", Static).update(f"Clicked {self.clicks} times")


if __name__ == "__main__":
    CounterApp().run()  # or .run(inline=True) to draw below the prompt

See it in a real stack

No architecture uses Textual yet. Drop it into the playground and wire it up yourself.Open playground →

Worth exploring

WebsiteAwesome TUIsJustin Garrison and contributors, on GitHubA community-maintained list of more than 700 terminal apps and libraries in 13 sections, from dashboards, editors and Docker or Kubernetes front ends to games, plus TUI libraries grouped by language (Python, Go, C, C++, Java, .NET, Rust and others). Entries must be interactive, maintained and not just wrappers around tools like fzf; they arrive by pull request with one-line descriptions, and the maintainer says to run them at your own risk.
All resources →
Where it came from2021–2026

Will McGugan first wrote Rich, a Python library for colored and formatted terminal output, as a hobby project, then asked why not build an application framework on top of it. Textual’s first release, 0.1.0, went to PyPI in June 2021. The terminal offered only the basics (move the cursor, write colored text, read keys and mouse movements), so everything from a button to a text editor had to be built from scratch.

McGugan founded Textualize, Inc. and worked on Textual full time with a small team. By late 2021 the framework took its styling from CSS, separating an app’s layout and design from its logic. Later work added inline apps, serving apps to web browsers, and in December 2024 a 1.0 release.

In May 2025 McGugan announced that Textualize would wrap up within weeks: Textual had been “a solution in search of a problem”, and the team hadn’t found a shared pain point to build a business around. Textual became a community project, still maintained by McGugan, who then built Toad (“Textual Code”), a terminal front end for AI coding agents, on top of it.

Why it’s built this way

CSS for styling
Keeping styles out of Python avoids tangled UI code, lets developers restyle built-in and third-party widgets as easily as their own, and makes live editing of the design possible.
Build on Rich’s segments
Treating the screen as a 2D array of characters breaks down with double-width characters such as emoji and Asian scripts. Working with Rich’s styled segments, and splitting them into chops, made overlapping widgets tractable.
asyncio throughout
Every widget is powered by an async task and handlers can be coroutines, so an app can await network libraries such as httpx, and workers run slow jobs without blocking the UI.
Serve the terminal protocol to browsers
Rather than a separate web renderer, textual-serve pipes the app’s escape codes over a WebSocket to xterm.js, with extra APIs for links and file downloads, so one app works in a terminal and a browser.

Timeline

  1. 2021

    Textual 0.1.0 published on PyPI (June); Textual CSS in development (November)

  2. 2024

    Browser features for apps served with textual-serve (September)

  3. 2024

    Textual 1.0 (December)

  4. 2025

    Textualize, the company, announces it will wrap up; Textual continues as open source (May)

  5. 2025

    McGugan releases Toad, built on Textual (December)

  6. 2026

    Textual 8.0 (February); 8.2.8 (June)

Sources12 · facts checked 13 October 2026

Improve this page on GitHub