Skip to content


A TextLog is a widget which displays scrollable content that may be appended to in realtime.

Call TextLog.write with a string or Rich Renderable to write content to the end of the TextLog. Call TextLog.clear to clear the content.

  • Focusable
  • Container


The example below shows each placeholder variant.

TextLogApp │   │   previous_value=next(iter_values) │   exceptStopIteration: │   │   return │   first=True▅▅ │   forvalueiniter_values: │   │   yieldfirst,False,previous_value │   │   first=False │   │   previous_value=value │   yieldfirst,True,previous_value ┏━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━┓ laneswimmer             country      time  ┡━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━┩ 4   Joseph Schooling    Singapore    50.39 2   Michael Phelps      United States51.14 5   Chad le Clos        South Africa 51.14 6   László Cseh         Hungary      51.14 3   Li Zhuhao           China        51.26 8   Mehdy Metella       France       51.58 7   Tom Shields         United States51.73 1   Aleksandr SadovnikovRussia       51.84 └──────┴──────────────────────┴───────────────┴───────┘ Write text or any Rich renderable! Key(key='H'character='H'name='upper_h'is_printable=True) Key(key='i'character='i'name='i'is_printable=True)

import csv
import io

from rich.table import Table
from rich.syntax import Syntax

from import App, ComposeResult
from textual import events
from textual.widgets import TextLog

CSV = """lane,swimmer,country,time
4,Joseph Schooling,Singapore,50.39
2,Michael Phelps,United States,51.14
5,Chad le Clos,South Africa,51.14
6,László Cseh,Hungary,51.14
3,Li Zhuhao,China,51.26
8,Mehdy Metella,France,51.58
7,Tom Shields,United States,51.73
1,Aleksandr Sadovnikov,Russia,51.84"""

CODE = '''\
def loop_first_last(values: Iterable[T]) -> Iterable[tuple[bool, bool, T]]:
    """Iterate and generate a tuple with a flag for first and last value."""
    iter_values = iter(values)
        previous_value = next(iter_values)
    except StopIteration:
    first = True
    for value in iter_values:
        yield first, False, previous_value
        first = False
        previous_value = value
    yield first, True, previous_value\

class TextLogApp(App):
    def compose(self) -> ComposeResult:
        yield TextLog(highlight=True, markup=True)

    def on_ready(self) -> None:
        """Called  when the DOM is ready."""
        text_log = self.query_one(TextLog)

        text_log.write(Syntax(CODE, "python", indent_guides=True))

        rows = iter(csv.reader(io.StringIO(CSV)))
        table = Table(*next(rows))
        for row in rows:

        text_log.write("[bold magenta]Write text or any Rich renderable!")

    def on_key(self, event: events.Key) -> None:
        """Write Key events to log."""
        text_log = self.query_one(TextLog)

if __name__ == "__main__":
    app = TextLogApp()

Reactive Attributes

Name Type Default Description
highlight bool False Automatically highlight content.
markup bool False Apply Rich console markup.
max_lines int None Maximum number of lines in the log or None for no maximum.
min_width int 78 Minimum width of renderables.
wrap bool False Enable word wrapping.


This widget sends no messages.

See Also