Skip to content

Spectre.Console

Repl.Spectre integrates Spectre.Console into Repl. Your handlers return plain .NET objects; Spectre.Console renders them as rich terminal output.

Terminal window
dotnet add package Repl.Spectre
var app = ReplApp.Create().UseDefaultInteractive();
app.UseSpectreConsole(); // rich prompts + renderables in one call

Return a Spectre.Console IRenderable directly from your handler:

app.Map("contacts table", static (IContactStore store) =>
{
var table = new Table()
.AddColumn("Id")
.AddColumn("Name")
.AddColumn("Email");
foreach (var c in store.All())
table.AddRow(c.Id.ToString(), c.Name, c.Email);
return table;
});
┌────┬───────┬──────────────────────┐
│ Id │ Name │ Email │
├────┼───────┼──────────────────────┤
│ 1 │ Alice │ alice@example.com │
│ 2 │ Bob │ bob@example.com │
└────┴───────┴──────────────────────┘
app.Map("stats chart", static (IContactStore store) =>
{
var chart = new BarChart()
.Width(60)
.Label("Contacts by domain");
foreach (var g in store.All().GroupBy(c => c.Email.Split('@')[1]))
chart.AddItem(g.Key, g.Count(), Color.Blue);
return chart;
});
using Repl.Interaction;
app.Map("import {file}", static async (string file, IReplInteractionChannel ui, CancellationToken ct) =>
{
var lines = await File.ReadAllLinesAsync(file, ct);
for (int i = 0; i < lines.Length; i++)
{
await ImportLine(lines[i], ct);
await ui.WriteProgressAsync($"Importing… {i + 1}/{lines.Length}", (double)(i + 1) / lines.Length, ct);
}
return Results.Success($"Imported {lines.Length} records.");
});

With UseSpectreConsole(), progress renders as a live Spectre.Console Progress bar.

UseSpectreConsole() upgrades all IReplInteractionChannel prompts to Spectre.Console widgets automatically:

using Repl.Interaction;
app.Map("add", static async (IReplInteractionChannel ui, IContactStore store, CancellationToken ct) =>
{
var name = await ui.AskTextAsync("name", "Full name:", ct: ct);
var email = await ui.AskTextAsync("email", "Email address:", ct: ct);
store.Add(name, email);
return Results.Success($"Added {name}.");
});

No handler changes are needed when switching between terminals — UseSpectreConsole() upgrades the rendering layer and all prompts degrade gracefully on plain terminals.

Any Spectre.Console renderable works as a return value: Table, Panel, Tree, BarChart, BreakdownChart, Calendar, JsonText, Markup, FigletText, TextPath, Grid, Columns, Rule.

Spectre.Console detects terminal capabilities automatically. On terminals without color support, output degrades gracefully to plain text.


  • Interactivity — the IReplInteractionChannel API and how Spectre upgrades each prompt type.
  • Best Practices — output routing, why AnsiConsole static must be avoided, and IAnsiConsole injection.