Live Display

Update and refresh any renderable content dynamically in real-time

The LiveDisplay renders content that can be updated in place without scrolling the console, perfect for dashboards, real-time monitoring, and dynamic status displays.

Screenshot

When to Use

Use LiveDisplay when you need to update arbitrary content in place without creating new output lines. Common scenarios:

  • Custom dashboards: Display real-time metrics, server stats, or system monitors with any widget combination
  • Dynamic tables: Build tables incrementally or update existing rows as data changes
  • Status transitions: Show multi-step processes with changing panels or formatted text
  • Real-time data: Update charts, gauges, or custom visualizations continuously

For progress tracking with multiple tasks, use Progress instead. For simple spinner animations, use Status.

Caution

Live display is not thread safe. Using it together with other interactive components such as prompts, progress displays, or status displays is not supported.

Basic Usage

Create a live display by passing any renderable to AnsiConsole.Live(), then update it within the context.

var table = new Table();
table.AddColumn("Status");
table.AddRow("Starting...");
  
AnsiConsole.Live(table)
    .Start(ctx =>
    {
        Thread.Sleep(1000);
        table.AddRow("Processing...");
        ctx.Refresh();
  
        Thread.Sleep(1000);
        table.AddRow("Complete!");
        ctx.Refresh();
  
        Thread.Sleep(1000);
    });

Updating Content

Modifying Mutable Renderables

Modify properties of mutable widgets like Table, then call ctx.Refresh() to update the display.

var table = new Table()
    .AddColumn("Server")
    .AddColumn("Status")
    .AddColumn("Uptime");
  
AnsiConsole.Live(table)
    .Start(ctx =>
    {
        var servers = new[]
        {
            ("web-01", "[green]Online[/]", "99.9%"),
            ("web-02", "[green]Online[/]", "99.8%"),
            ("db-01", "[green]Online[/]", "100%"),
            ("cache-01", "[yellow]Degraded[/]", "95.2%"),
            ("api-01", "[green]Online[/]", "99.7%")
        };
  
        foreach (var (server, status, uptime) in servers)
        {
            table.AddRow(server, status, uptime);
            ctx.Refresh();
            Thread.Sleep(500);
        }
    });

Replacing the Target

Use ctx.UpdateTarget() to completely replace the displayed renderable with a different widget.

AnsiConsole.Live(new Text("Initializing..."))
    .Start(ctx =>
    {
        Thread.Sleep(1000);
  
        // Replace with a panel
        var panel = new Panel("Loading configuration...")
            .Header("Step 1")
            .BorderColor(Color.Blue);
        ctx.UpdateTarget(panel);
        Thread.Sleep(1000);
  
        // Replace with a different panel
        var panel2 = new Panel("Connecting to database...")
            .Header("Step 2")
            .BorderColor(Color.Yellow);
        ctx.UpdateTarget(panel2);
        Thread.Sleep(1000);
  
        // Replace with final panel
        var panel3 = new Panel("[green]Ready![/]")
            .Header("Complete")
            .BorderColor(Color.Green);
        ctx.UpdateTarget(panel3);
        Thread.Sleep(1000);
    });

Displaying Panels

Wrap dynamic content in panels for polished status displays.

var table = new Table()
    .Border(TableBorder.None)
    .AddColumn("Metric")
    .AddColumn("Value");
  
var panel = new Panel(table)
    .Header("System Monitor")
    .BorderColor(Color.Cyan)
    .RoundedBorder();
  
AnsiConsole.Live(panel)
    .Start(ctx =>
    {
        for (int i = 0; i < 10; i++)
        {
            table.Rows.Clear();
            table.AddRow("CPU Usage", $"{Random.Shared.Next(10, 80)}%");
            table.AddRow("Memory", $"{Random.Shared.Next(2, 8)} GB / 16 GB");
            table.AddRow("Network", $"{Random.Shared.Next(100, 999)} MB/s");
            table.AddRow("Uptime", $"{i + 1} seconds");
  
            ctx.Refresh();
            Thread.Sleep(1000);
        }
    });

Handling Overflow

When content exceeds the console height, LiveDisplay provides several overflow strategies.

Ellipsis Mode

Show an ellipsis indicator when content is truncated.

var table = new Table()
    .AddColumn("Line");
  
AnsiConsole.Live(table)
    .Overflow(VerticalOverflow.Ellipsis)
    .Start(ctx =>
    {
        for (int i = 1; i <= 100; i++)
        {
            table.AddRow($"Line {i}");
            ctx.Refresh();
            Thread.Sleep(50);
        }
    });

Crop Mode

Silently crop content that doesn't fit, combined with cropping direction control.

var table = new Table()
    .AddColumn("Line");
  
AnsiConsole.Live(table)
    .Overflow(VerticalOverflow.Crop)
    .Start(ctx =>
    {
        for (int i = 1; i <= 100; i++)
        {
            table.AddRow($"Line {i}");
            ctx.Refresh();
            Thread.Sleep(50);
        }
    });

Visible Mode

Allow content to scroll naturally when it exceeds console height.

var table = new Table()
    .AddColumn("Line");
  
AnsiConsole.Live(table)
    .Overflow(VerticalOverflow.Visible)
    .Start(ctx =>
    {
        for (int i = 1; i <= 30; i++)
        {
            table.AddRow($"Line {i}");
            ctx.Refresh();
            Thread.Sleep(100);
        }
    });

Cropping Direction

Control which part of overflowing content remains visible.

Crop from Top

Keep the most recent content visible by removing old content from the top.

var table = new Table()
    .AddColumn("Log Entry");
  
AnsiConsole.Live(table)
    .Overflow(VerticalOverflow.Crop)
    .Cropping(VerticalOverflowCropping.Top)
    .Start(ctx =>
    {
        for (int i = 1; i <= 50; i++)
        {
            table.AddRow($"[dim]{DateTime.Now:HH:mm:ss}[/] Log entry {i}");
            ctx.Refresh();
            Thread.Sleep(100);
        }
    });

Crop from Bottom

Keep the initial content visible by removing new content from the bottom.

var table = new Table()
    .AddColumn("Task Queue");
  
AnsiConsole.Live(table)
    .Overflow(VerticalOverflow.Crop)
    .Cropping(VerticalOverflowCropping.Bottom)
    .Start(ctx =>
    {
        for (int i = 1; i <= 50; i++)
        {
            table.AddRow($"Task {i}: Processing");
            ctx.Refresh();
            Thread.Sleep(100);
        }
    });

Auto Clear

Remove the live display from the console when the context completes.

var spinner = new Table()
    .AddColumn("Status")
    .AddRow("[blue]Processing...[/]");
  
AnsiConsole.Live(spinner)
    .AutoClear(true)
    .Start(ctx =>
    {
        for (int i = 1; i <= 5; i++)
        {
            spinner.Rows.Clear();
            spinner.AddRow($"[blue]Processing step {i}/5...[/]");
            ctx.Refresh();
            Thread.Sleep(800);
        }
    });
  
AnsiConsole.WriteLine("Display cleared - task complete!");

Async Operations

Use StartAsync() for asynchronous work within the live display context.

var status = new Table()
    .AddColumn("Operation")
    .AddColumn("Status");
  
await AnsiConsole.Live(status)
    .StartAsync(async ctx =>
    {
        var operations = new[]
        {
            "Fetching data",
            "Processing records",
            "Updating database",
            "Generating report",
            "Sending notifications"
        };
  
        foreach (var operation in operations)
        {
            status.AddRow(operation, "[yellow]In Progress[/]");
            ctx.Refresh();
  
            await Task.Delay(1000);
  
            status.Rows.Update(status.Rows.Count - 1, 1, new Text("[green]Complete[/]"));
            ctx.Refresh();
        }
    });

Returning Values

Return results from the live display context using the generic Start<T>() method.

var result = AnsiConsole.Live(new Text("Processing..."))
    .Start(ctx =>
    {
        int total = 0;
  
        for (int i = 1; i <= 10; i++)
        {
            total += i;
            ctx.UpdateTarget(new Text($"Processing: {i}/10 (Total: {total})"));
            Thread.Sleep(300);
        }
  
        ctx.UpdateTarget(new Text("[green]Complete![/]"));
        Thread.Sleep(500);
  
        return total;
    });
  
AnsiConsole.WriteLine($"Final result: {result}");

Combining Widgets

Create sophisticated dashboards by combining multiple widgets in layouts.

// Create status table
var statusTable = new Table()
    .Border(TableBorder.Rounded)
    .BorderColor(Color.Cyan)
    .AddColumn("Service")
    .AddColumn("Status")
    .AddColumn("Requests/sec");
  
// Create metrics table
var metricsTable = new Table()
    .Border(TableBorder.Rounded)
    .BorderColor(Color.Yellow)
    .AddColumn("Metric")
    .AddColumn("Current")
    .AddColumn("Average");
  
// Create layout with both tables
var layout = new Layout("Root")
    .SplitRows(
        new Layout("Header"),
        new Layout("Body").SplitColumns(
            new Layout("Status"),
            new Layout("Metrics")
        )
    );
  
layout["Header"].Update(
    new Panel("[bold cyan]Real-Time Dashboard[/]")
        .BorderColor(Color.Blue)
        .Padding(1, 0)
);
  
layout["Status"].Update(new Panel(statusTable).Header("Services"));
layout["Metrics"].Update(new Panel(metricsTable).Header("System Metrics"));
  
AnsiConsole.Live(layout)
    .Start(ctx =>
    {
        for (int i = 0; i < 15; i++)
        {
            // Update status table
            statusTable.Rows.Clear();
            statusTable.AddRow("API Gateway", "[green]Healthy[/]", $"{Random.Shared.Next(100, 500)}");
            statusTable.AddRow("Auth Service", "[green]Healthy[/]", $"{Random.Shared.Next(50, 200)}");
            statusTable.AddRow("Database", i > 10 ? "[yellow]Degraded[/]" : "[green]Healthy[/]", $"{Random.Shared.Next(200, 800)}");
            statusTable.AddRow("Cache", "[green]Healthy[/]", $"{Random.Shared.Next(1000, 3000)}");
  
            // Update metrics table
            metricsTable.Rows.Clear();
            metricsTable.AddRow("CPU", $"{Random.Shared.Next(20, 75)}%", "45%");
            metricsTable.AddRow("Memory", $"{Random.Shared.Next(4, 12)} GB", "8 GB");
            metricsTable.AddRow("Disk I/O", $"{Random.Shared.Next(10, 90)} MB/s", "45 MB/s");
            metricsTable.AddRow("Network", $"{Random.Shared.Next(100, 500)} MB/s", "250 MB/s");
  
            ctx.Refresh();
            Thread.Sleep(1000);
        }
    });

See Also

API Reference

Represents a live display.

Constructors

LiveDisplay(IAnsiConsole console, IRenderable target)

Initializes a new instance of the class.

Parameters:

console (IAnsiConsole)
The console.
target (IRenderable)
The target renderable to update.

Properties

AutoClear : bool

Gets or sets a value indicating whether or not the live display should be cleared when it's done. Defaults to false.

Cropping : VerticalOverflowCropping

Gets or sets the vertical overflow cropping strategy.

Overflow : VerticalOverflow

Gets or sets the vertical overflow strategy.

Methods

Start(Action<LiveDisplayContext> action)

Starts the live display.

Parameters:

action (Action<LiveDisplayContext>)
The action to execute.
T Start(Func<LiveDisplayContext, T> func)

Parameters:

func (Func<LiveDisplayContext, T>)
Task StartAsync(Func<LiveDisplayContext, Task> func)

Starts the live display.

Parameters:

func (Func<LiveDisplayContext, Task>)
The action to execute.

Returns:

The result.

Task<T> StartAsync(Func<LiveDisplayContext, Task<T>> func)

Parameters:

func (Func<LiveDisplayContext, Task<T>>)

Extension Methods

LiveDisplay AutoClear(bool enabled)

Sets whether or not auto clear is enabled. If enabled, the live display will be cleared when done.

Parameters:

enabled (bool)
Whether or not auto clear is enabled.

Returns:

The same instance so that multiple calls can be chained.

LiveDisplay Cropping(VerticalOverflowCropping cropping)

Sets the vertical overflow cropping strategy.

Parameters:

cropping (VerticalOverflowCropping)
The overflow cropping strategy to use.

Returns:

The same instance so that multiple calls can be chained.

LiveDisplay Overflow(VerticalOverflow overflow)

Sets the vertical overflow strategy.

Parameters:

overflow (VerticalOverflow)
The overflow strategy to use.

Returns:

The same instance so that multiple calls can be chained.