# How Rich Implements Live Updates with the Live Class for Dynamic Content

> Learn how Rich implements live updates with the Live class for dynamic content. Discover its thread-safe rendering, cursor management, and Console integration.

- Repository: [Textualize/rich](https://github.com/Textualize/rich)
- Tags: internals
- Published: 2026-03-06

---

**Rich implements live updates through the `Live` class in [`rich/live.py`](https://github.com/Textualize/rich/blob/main/rich/live.py), which coordinates a daemonic `_RefreshThread` for periodic redraws, a `LiveRender` helper to manage display state and cursor positioning, and deep integration with the `Console` class to handle terminal control sequences, nested live stacks, and thread-safe rendering.**

The `Live` class in Textualize/rich enables developers to create dynamic terminal interfaces that update in real-time without flooding the console with new lines. This implementation centers around thread-safe rendering hooks, automatic refresh cycles, and sophisticated terminal control management that preserves cursor state while redirecting standard I/O streams.

## Architecture of the Live Update System

Four core components work together to enable live terminal updates:

- **`Live`** ([`rich/live.py`](https://github.com/Textualize/rich/blob/main/rich/live.py)): The public API that manages the display lifecycle, optional auto-refresh, and I/O redirection
- **`_RefreshThread`** (`rich/live.py#L22-L39`): A daemon thread that periodically triggers redraws when `auto_refresh=True`
- **`LiveRender`** (`rich/live_render.py#L13`): A low-level helper that stores the current renderable, handles vertical overflow, and manages cursor restoration
- **`Console`** ([`rich/console.py`](https://github.com/Textualize/rich/blob/main/rich/console.py)): The central rendering engine providing live-stack management via `set_live`, `clear_live`, and render hook orchestration

## Constructing a Live Instance

When instantiating `Live` (`__init__` at `rich/live.py#L57-L70`), the class initializes several critical components:

1. Stores the initial renderable in `self._renderable`
2. Acquires a `Console` instance (defaulting via `get_console()`)
3. Configures screen mode, I/O redirection, and thread-locking via `self._lock`
4. Creates a `LiveRender` object at `rich/live.py#L92-L95` to hold the current display state and handle dimensional tracking

## Starting the Live Display

The `Live.start()` method (`rich/live.py#L111-L144`) activates the live update mechanism through several coordinated steps.

### Console Registration and Nesting

The method calls `self.console.set_live(self)` to register with the console's internal live stack. If another live display is active, the instance is marked as nested (`self._nested = True`), enabling layered dynamic content.

### Terminal State Management

When `screen=True`, the console switches to an alternate screen buffer via `self.console.set_alt_screen(True)`. The cursor is hidden using `self.console.show_cursor(False)`, and standard output streams are redirected through `FileProxy` objects (from [`rich/file_proxy.py`](https://github.com/Textualize/rich/blob/main/rich/file_proxy.py)) via the internal `_enable_redirect_io()` method.

### Render Hook Installation

The live instance inserts itself into the console's rendering pipeline through `self.console.push_render_hook(self)`, ensuring its content renders atomically before normal console output.

### Auto-Refresh Initialization

If `auto_refresh` is enabled, the method spawns a `_RefreshThread` at the configured `refresh_per_second` frequency and starts the background update loop.

## The Refresh Cycle

`Live.refresh()` (`rich/live.py#L44-L78`) serves as the heart of the update mechanism:

1. **Thread Safety**: Acquires `self._lock` to prevent concurrent modifications during the render cycle
2. **Renderable Resolution**: Calls `self._live_render.set_renderable(self.renderable)`, where the `renderable` property (`rich/live.py#L21-L28`) resolves nested live stacks into a `Group` and wraps content in a `Screen` when using alternate screen mode
3. **Nesting Propagation**: If marked as nested, delegates to the first live instance's `refresh()` to propagate changes upward through the stack
4. **Output Rendering**:
   - In Jupyter environments, updates through `ipywidgets.Output` via `self.ipy_widget`
   - In terminals, prints `Control()` escape sequences (from [`rich/control.py`](https://github.com/Textualize/rich/blob/main/rich/control.py)) to clear the previous live block before drawing new content
   - In non-interactive modes, outputs control sequences to ensure file compatibility

The `_RefreshThread` daemon wakes at the specified frequency and invokes `live.refresh()` until the display stops.

## Updating Dynamic Content

Users can modify displayed content through two primary pathways.

### Direct Update Method

`live.update(new_renderable, refresh=True)` (`rich/live.py#L30-L43`) replaces `self._renderable` and optionally triggers an immediate redraw via the internal `_refresh` flag.

### Callable Renderable Pattern

Passing a callable to `get_renderable` at construction allows `Live.get_renderable()` (`rich/live.py#L103-L109`) to invoke it dynamically during each refresh cycle. This enables state-based updates without explicit method calls, as the callable executes fresh on every refresh tick.

## Stopping and Cleanup

`Live.stop()` (`rich/live.py#L45-L81`) reverses the startup sequence to restore terminal state:

1. Clears the live area via `self.console.clear_live()`
2. Terminates the background `_RefreshThread` if active
3. Restores cursor visibility, disables I/O redirection, and removes the render hook via `self.console.pop_render_hook(self)`
4. In transient mode, restores cursor position using `self._live_render.restore_cursor()`
5. Prints a final newline to advance the prompt (unless using an alternate screen buffer)

## Console Integration and the Live Stack

The `Console` class maintains a `_live_stack` tracking active live displays. This stack architecture supports nesting, where the first `Live` instance renders a `Group` containing all nested renderables. Console methods `set_live`, `clear_live`, `push_render_hook`, and `pop_render_hook` orchestrate the rendering order, ensuring live content appears above standard console output while maintaining terminal state consistency across nested contexts.

## Practical Implementation Example

```python
from rich.live import Live
from rich.console import Console
from rich.table import Table
import time

console = Console()

def make_table() -> Table:
    table = Table(title="Progress")
    table.add_column("Step")
    table.add_column("Status")
    for i in range(5):
        table.add_row(f"Task {i+1}", "[green]✓[/green]" if i < 3 else "[yellow]…[/yellow]")
    return table

with Live(make_table(), console=console, refresh_per_second=2) as live:
    for i in range(5, 10):
        time.sleep(1)
        live.update(make_table())

```

This example demonstrates:
- **Context manager usage**: `__enter__` starts the display, `__exit__` triggers cleanup
- **Background refresh**: `refresh_per_second=2` creates a 2Hz update thread
- **Content swapping**: `live.update()` replaces the renderable while maintaining terminal state

## Summary

- The `Live` class in [`rich/live.py`](https://github.com/Textualize/rich/blob/main/rich/live.py) provides the primary API for dynamic terminal updates, managing construction, refresh cycles, and cleanup through thread-safe operations
- Background updates are handled by `_RefreshThread`, a daemon thread that calls `refresh()` at configurable intervals defined by `refresh_per_second`
- `LiveRender` ([`rich/live_render.py`](https://github.com/Textualize/rich/blob/main/rich/live_render.py)) manages the actual display state, handling cursor positioning, dimensional tracking, and overflow management
- The `Console` class coordinates multiple live displays through a `_live_stack`, enabling nested live contexts and proper render ordering via `push_render_hook` and `pop_render_hook`
- Terminal state preservation is achieved through cursor hiding, alternate screen buffers, and I/O redirection through `FileProxy` objects

## Frequently Asked Questions

### How does Rich prevent flickering during live updates?

Rich minimizes flickering by using terminal control sequences defined in [`rich/control.py`](https://github.com/Textualize/rich/blob/main/rich/control.py) to clear only the specific region occupied by the live display rather than the entire screen. The `LiveRender` class tracks the exact dimensions of the previous render, allowing `Live.refresh()` to position the cursor precisely and overwrite only the necessary lines, maintaining smooth visual updates without full-screen clears.

### Can multiple Live instances run simultaneously in Rich?

Yes, Rich supports nested live displays through the console's `_live_stack`. When starting a new `Live` instance while another is active, `Live.start()` detects the existing context via `self.console.set_live(self)` and marks the new instance as nested. The first live instance in the stack renders a `Group` containing all nested renderables, ensuring proper layering and update propagation through the live stack.

### What is the difference between `live.update()` and `live.refresh()`?

`live.update()` (`rich/live.py#L30-L43`) replaces the internal renderable stored in `self._renderable` and optionally triggers a refresh, while `live.refresh()` (`rich/live.py#L44-L78`) redraws the current renderable without changing the underlying content. Use `update()` when switching to new content, and `refresh()` when the same object has been modified in-place or when using a callable `get_renderable` that returns dynamic state based on external conditions.

### How does Rich handle live updates in Jupyter notebooks?

When running in a Jupyter environment, Rich detects the context and renders live updates through an `ipywidgets.Output` widget instead of terminal control sequences. The `Live.refresh()` method checks for the presence of `self.ipy_widget` and updates the widget content directly, allowing dynamic displays to function within notebook cells without requiring terminal emulation or cursor manipulation.