Code of the Day
AdvancedText User Interfaces

Live dashboards

Build a self-refreshing Textual dashboard using DataTable and set_interval.

UtilitiesAdvanced10 min read
Recommended first
By the end of this lesson you will be able to:
  • 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 table

Rows 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:

Python — editable, runs in your browser

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.

Finished reading? Mark it complete to track your progress.

On this page