| name | spectre-console |
| description | Use when building rich terminal UIs in .NET with Spectre.Console. Covers tables, trees, progress bars, prompts, live rendering, markup formatting, and the Spectre.Console.Cli command framework.
USE FOR: rendering tables and grids in the terminal, progress bars and spinners, interactive prompts and selections, colorized and styled console output, building CLI apps with Spectre.Console.Cli
DO NOT USE FOR: executing external processes (use cliwrap), argument parsing without UI (use commandline-cheatsheet), building web UIs (use Blazor), logging output (use ILogger with console provider)
|
| license | MIT |
| metadata | {"displayName":"Spectre.Console","author":"Tyler-R-Kendrick","version":"1.0.0"} |
| compatibility | claude, copilot, cursor |
| references | [{"title":"Spectre.Console Documentation","url":"https://spectreconsole.net"},{"title":"Spectre.Console GitHub Repository","url":"https://github.com/spectreconsole/spectre.console"},{"title":"Spectre.Console NuGet Package","url":"https://www.nuget.org/packages/Spectre.Console"}] |
Spectre.Console
Overview
Spectre.Console is a .NET library for building beautiful, modern console applications with rich text rendering, tables, trees, progress bars, interactive prompts, and live-updating displays. It also provides Spectre.Console.Cli, a command framework for building structured CLI applications with dependency injection, type-safe settings, and automatic help generation.
NuGet Packages
dotnet add package Spectre.Console
dotnet add package Spectre.Console.Cli
dotnet add package Spectre.Console.Analyzer
Markup and Styled Text
using Spectre.Console;
AnsiConsole.MarkupLine("[bold green]Success![/] The operation completed.");
AnsiConsole.MarkupLine("[red]Error:[/] File [underline]config.json[/] not found.");
AnsiConsole.MarkupLine("[dim italic]This is dimmed italic text[/]");
AnsiConsole.MarkupLine("[rgb(255,165,0)]Orange text[/]");
AnsiConsole.MarkupLine("[#FF5733]Hex color[/]");
var userInput = "User said [hello]";
AnsiConsole.MarkupLine($"Message: {userInput.EscapeMarkup()}");
AnsiConsole.Write(new FigletText("My App")
.Centered()
.Color(Color.Aqua));
AnsiConsole.Write(new Rule("[yellow]Configuration[/]").RuleStyle("grey"));
Tables
using Spectre.Console;
var table = new Table()
.Title("[bold underline]Server Status[/]")
.Border(TableBorder.Rounded)
.BorderColor(Color.Grey);
table.AddColumn(new TableColumn("[bold]Service[/]").Centered());
table.AddColumn(new TableColumn("[bold]Status[/]").Centered());
table.AddColumn(new TableColumn("[bold]Uptime[/]").RightAligned());
table.AddColumn(new TableColumn("[bold]CPU[/]").RightAligned());
table.AddRow("API Gateway", "[green]Running[/]", "14d 3h", "12%");
table.AddRow("Auth Service", "[green]Running[/]", "14d 3h", "8%");
table.AddRow("Worker", "[yellow]Degraded[/]", "2d 1h", "87%");
table.AddRow("Cache", "[red]Down[/]", "0h 0m", "0%");
AnsiConsole.Write(table);
var outerTable = new Table().AddColumn("Details");
var innerTable = new Table()
.AddColumn("Key")
.AddColumn("Value")
.AddRow("Version", "2.1.0")
.AddRow("Environment", "Production");
outerTable.AddRow(innerTable);
AnsiConsole.Write(outerTable);
Trees
using Spectre.Console;
var root = new Tree("[bold yellow]Solution[/]");
var srcNode = root.AddNode("[blue]src/[/]");
var apiNode = srcNode.AddNode("[blue]MyApp.Api/[/]");
apiNode.AddNode("Program.cs");
apiNode.AddNode("Startup.cs");
apiNode.AddNode("[dim]appsettings.json[/]");
var domainNode = srcNode.AddNode("[blue]MyApp.Domain/[/]");
domainNode.AddNode("Entities/");
domainNode.AddNode("Services/");
var testNode = root.AddNode("[blue]tests/[/]");
testNode.AddNode("MyApp.Tests/");
AnsiConsole.Write(root);
Progress Bars
using Spectre.Console;
await AnsiConsole.Progress()
.AutoClear(false)
.Columns(new ProgressColumn[]
{
new TaskDescriptionColumn(),
new ProgressBarColumn(),
new PercentageColumn(),
new RemainingTimeColumn(),
new SpinnerColumn()
})
.StartAsync(async ctx =>
{
var downloadTask = ctx.AddTask("[green]Downloading packages[/]", maxValue: 100);
var buildTask = ctx.AddTask("[blue]Building project[/]", maxValue: 100);
var testTask = ctx.AddTask("[yellow]Running tests[/]", maxValue: 100);
while (!ctx.IsFinished)
{
await Task.Delay(50);
if (!downloadTask.IsFinished)
downloadTask.Increment(2.5);
else if (!buildTask.IsFinished)
buildTask.Increment(1.8);
else if (!testTask.IsFinished)
testTask.Increment(3.0);
}
});
Status Spinner
using Spectre.Console;
await AnsiConsole.Status()
.Spinner(Spinner.Known.Dots)
.SpinnerStyle(Style.Parse("green bold"))
.StartAsync("Connecting to database...", async ctx =>
{
await Task.Delay(1000);
ctx.Status("Running migrations...");
ctx.Spinner(Spinner.Known.Star);
await Task.Delay(2000);
ctx.Status("Seeding data...");
await Task.Delay(1500);
});
AnsiConsole.MarkupLine("[green]Done![/]");
Interactive Prompts
using Spectre.Console;
var name = AnsiConsole.Ask<string>("What is your [green]name[/]?");
var city = AnsiConsole.Ask("What [green]city[/] do you live in?", "Seattle");
var password = AnsiConsole.Prompt(
new TextPrompt<string>("Enter [red]password[/]:")
.Secret()
.Validate(p => p.Length >= 8
? ValidationResult.Success()
: ValidationResult.Error("Password must be at least 8 characters")));
var proceed = AnsiConsole.Confirm("Deploy to [red]production[/]?", defaultValue: false);
var framework = AnsiConsole.Prompt(
new SelectionPrompt<string>()
.Title("Select a [green]framework[/]:")
.PageSize(10)
.AddChoiceGroup("Frontend", "React", "Vue", "Angular")
.AddChoiceGroup("Backend", "ASP.NET Core", "FastAPI", "Express"));
var features = AnsiConsole.Prompt(
new MultiSelectionPrompt<string>()
.Title("Select [green]features[/] to enable:")
.Required()
.InstructionsText("[grey](Press [blue]<space>[/] to toggle, [green]<enter>[/] to accept)[/]")
.AddChoices("Authentication", , ,
, , ));
Console.WriteLine();
Live Display
Update the terminal in real time with live rendering.
using Spectre.Console;
var table = new Table().AddColumn("Time").AddColumn("Event");
await AnsiConsole.Live(table)
.AutoClear(false)
.Overflow(VerticalOverflow.Ellipsis)
.StartAsync(async ctx =>
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(500);
table.AddRow(
DateTime.Now.ToString("HH:mm:ss"),
$"[green]Event {i + 1}[/] processed successfully");
ctx.Refresh();
}
});
Panels and Layout
using Spectre.Console;
var panel = new Panel(
new Markup("[bold]Application Status[/]\n\nAll systems operational."))
{
Header = new PanelHeader("[green] Dashboard [/]"),
Border = BoxBorder.Rounded,
Padding = new Padding(2, 1),
BorderStyle = new Style(Color.Green)
};
AnsiConsole.Write(panel);
var chart = new BarChart()
.Label("[bold underline]Request Count by Endpoint[/]")
.CenterLabel()
.AddItem("GET /api/users", 1200, Color.Green)
.AddItem("POST /api/orders", 850, Color.Blue)
.AddItem("GET /api/products", 2100, Color.Yellow)
.AddItem("DELETE /api/sessions", 340, Color.Red);
AnsiConsole.Write(chart);
Exception Formatting
using Spectre.Console;
try
{
throw new InvalidOperationException("Database connection failed",
new TimeoutException("Connection timed out after 30 seconds"));
}
catch (Exception ex)
{
AnsiConsole.WriteException(ex, new ExceptionSettings
{
Format = ExceptionFormats.ShortenEverything | ExceptionFormats.ShowLinks,
Style = new ExceptionStyle
{
Message = new Style(Color.Red),
Exception = new Style(Color.Grey),
Method = new Style(Color.Yellow),
Path = new Style(Color.Blue)
}
});
}
Spectre.Console.Cli (Command Framework)
Build structured CLI applications with typed settings and dependency injection.
using Spectre.Console;
using Spectre.Console.Cli;
using System.ComponentModel;
var app = new CommandApp();
app.Configure(config =>
{
config.AddCommand<DeployCommand>("deploy")
.WithDescription("Deploy the application")
.WithExample("deploy", "--environment", "staging", "--verbose");
config.AddBranch("db", db =>
{
db.AddCommand<MigrateCommand>("migrate")
.WithDescription("Run database migrations");
db.AddCommand<SeedCommand>("seed")
.WithDescription("Seed the database");
});
});
return app.Run(args);
public class DeploySettings : CommandSettings
{
[CommandOption("-e|--environment")]
[Description("Target environment")]
[DefaultValue("staging")]
public string Environment { get; init; } = "staging";
[CommandOption("-v|--verbose")]
[Description("Enable verbose output")]
public bool Verbose { get; init; }
[CommandOption("--dry-run")]
[Description("Simulate deployment without making changes")]
public DryRun { ; ; }
{
validEnvs = [] { , , };
(!validEnvs.Contains(Environment.ToLower()))
ValidationResult.Error();
ValidationResult.Success();
}
}
: <>
{
{
(settings.Environment == && !settings.DryRun)
{
(!AnsiConsole.Confirm(, defaultValue: ))
;
}
AnsiConsole.Status()
.StartAsync(, ctx =>
{
ctx.Status();
Task.Delay();
ctx.Status();
Task.Delay();
ctx.Status();
Task.Delay();
});
AnsiConsole.MarkupLine();
;
}
}
Best Practices
- Use
AnsiConsole.MarkupLine with Spectre markup tags ([green], [bold], [underline]) instead of Console.Write with ANSI escape codes for portable colorized output that degrades gracefully on terminals without color support.
- Call
.EscapeMarkup() on any user-provided strings before interpolating them into markup templates to prevent markup injection (e.g., user input containing [red] would break rendering).
- Use
SelectionPrompt<T> and MultiSelectionPrompt<T> for interactive selection rather than asking users to type values manually, which reduces input errors and improves UX.
- Use
AnsiConsole.Status() with a spinner for short operations (under 10 seconds) and AnsiConsole.Progress() with named tasks for longer operations where users need to see which step is running.
- Override
Validate() on CommandSettings subclasses to enforce business rules (e.g., environment must be one of development/staging/production) with clear error messages before command execution.
- Use
config.AddBranch in Spectre.Console.Cli to group related subcommands (e.g., db migrate, db seed) rather than prefixing command names manually.
- Set
AutoClear(false) on Progress and Live renderers when the final output should remain visible after completion, rather than being cleared from the terminal.
- Use
Table.Border(TableBorder.Rounded) with BorderColor(Color.Grey) for a polished look; avoid TableBorder.None unless the data is already visually structured.
- Add the
Spectre.Console.Analyzer NuGet package to get compile-time warnings for common mistakes like unescaped markup and incorrect color names.
- Use
new ExceptionSettings { Format = ExceptionFormats.ShortenEverything } when displaying exceptions to users to reduce noise, and include ExceptionFormats.ShowLinks in development to get clickable file paths in supported terminals.