Live dashboards
Build a self-refreshing Textual dashboard using DataTable and set_interval.
- Populate a DataTable with columns and rows
- Use set_interval to schedule a periodic callback
- Update table contents live without flickering
A DataTable that updates itself every second is the TUI equivalent of a web
dashboard. Textual's set_interval schedules a callback on a timer; the callback
reads new data and rewrites the table rows. The framework handles re-rendering.
DataTable basics
DataTable is a scrollable, keyboard-navigable table widget. Columns are added
once; rows are added, replaced, or removed as data changes:
from textual.widgets import DataTable
# In compose():
table = DataTable()
table.add_columns("Name", "Status", "Uptime")
table.add_row("api-server", "running", "2h 14m")
table.add_row("worker-1", "running", "2h 14m")
table.add_row("worker-2", "stopped", "0m")
yield tableRows have a key parameter that lets you update a specific row by name:
table.add_row("api-server", "running", "2h 14m", key="api-server")
# Later:
table.update_row("api-server", "running", "3h 01m")set_interval
set_interval(seconds, callback) schedules a callback to run every seconds
seconds on the Textual event loop. Call it inside on_mount — the event that
fires when the app finishes mounting its widget tree:
def on_mount(self) -> None:
self.set_interval(1.0, self.refresh_data)
def refresh_data(self) -> None:
# Called every second
...The callback runs on the same thread as the rest of the app, so you can safely
query and update widgets from it. If the data fetch is slow (a network call, a
database query), run it in a worker thread and post the result back to the
UI thread — but for most CLI dashboards a fast local computation is fine.
A complete live dashboard
The demo below shows the structure. In a real terminal, the table would update every second. In the code runner, it renders once:
Updating cells vs rebuilding rows
Two strategies for live updates:
update_cell(row_key, column_key, value)— update one cell. Fast; no flicker. Use this when rows are stable and only values change.clear()+add_row()for each item — rebuild the table from scratch. Simple but causes a visible flash on each refresh. Use this when the set of rows itself changes (processes appear or disappear).
For most dashboards, the stable-rows approach with update_cell produces
the best user experience.
Header(show_clock=True) displays a live clock in the header bar — a tiny
but effective signal to users that the app is running and up to date. It
updates automatically; you do not need a timer for it.
Where to go next
Next: lab — TUI tool — build a complete TUI log viewer with auto-refresh, keyboard navigation, and an export key.