Skip to main content
ansicode
C#

ANSI escape codes in C# — Console.Write, Spectre.Console, raw \x1b

C# string literals support `\x1b` directly — `Console.Write("\x1b[31merror\x1b[0m")` is byte-clean on macOS / Linux and on Windows 10 1709+ / Windows Terminal / Windows 11, where Conhost parses VT by default. For anything richer than ad-hoc escapes, `Spectre.Console` is the de facto modern .NET TUI / styled-console library (markup syntax `[red]error[/]`, tables, progress, prompts) and `Pastel` gives you a one-liner extension method (`"hello".Pastel(Color.Red)`). On legacy Windows builds before 1607 you must flip `ENABLE_VIRTUAL_TERMINAL_PROCESSING` via a `SetConsoleMode` P/Invoke before the first write. Set `Console.OutputEncoding = Encoding.UTF8` to keep box-drawing and emoji intact.

Recommended libraries

  • Spectre.Console

    The de facto modern .NET console toolkit — markup syntax (`[red]error[/]`, `[bold yellow on blue]warn[/]`), tables, trees, progress, prompts, exception rendering. Auto-detects terminal capability (TrueColor / 256 / 16 / no-colour) and falls back gracefully. Emits standard ANSI under the hood.

  • Pastel

    Lightweight string-extension API: `"error".Pastel(Color.Red).PastelBg(Color.Black)`. Returns a string with the ANSI bytes baked in — drop it anywhere `Console.WriteLine` expects a string. No state, no init, supports TrueColor.

  • Crayon

    Fluent colour-DSL: `Output.Red("error")`, `Output.Bright.Blue("info")`. Composable, allocation-light, capability-gated via `NO_COLOR`. Small surface area when you don't need Spectre's full TUI machinery.

  • Sharprompt

    Interactive-prompt library — `Prompt.Select("Pick one", choices)`, `Prompt.Input<string>("Name")`, password, multi-select, autocomplete. Built on the same ANSI / VT primitives this site documents; pairs well with Spectre.Console for richer UIs.

Idiomatic patterns

Direct Console.Write with \x1b — UTF-8 first
using System;
using System.Text;

class Program
{
    static void Main()
    {
        // Box-drawing + emoji safety — defaults vary by OS / culture.
        Console.OutputEncoding = Encoding.UTF8;

        // \x1b is the ESC byte; C# string literals accept it verbatim.
        Console.WriteLine("\x1b[1;31merror:\x1b[0m permission denied");
        Console.WriteLine("\x1b[33mwarn:\x1b[0m deprecated flag");
        Console.WriteLine("\x1b[32mok:\x1b[0m 142 tests passed");
    }
}
Spectre.Console markup — `[red]error[/]`
using Spectre.Console;

// dotnet add package Spectre.Console

AnsiConsole.MarkupLine("[bold red]error:[/] permission denied");
AnsiConsole.MarkupLine("[yellow]warn:[/] deprecated flag");
AnsiConsole.MarkupLine("[green]ok:[/] 142 tests passed");

// Truecolor — RGB triplet or hex:
AnsiConsole.MarkupLine("[#ff8000]orange truecolor[/]");
AnsiConsole.MarkupLine("[rgb(255,128,0)]same, RGB form[/]");

// Tables and progress are first-class:
var table = new Table().AddColumns("File", "Status");
table.AddRow("app.log", "[green]ok[/]");
table.AddRow("db.log", "[red]missing[/]");
AnsiConsole.Write(table);
Capability gate — NO_COLOR + IsOutputRedirected
using System;

static bool AnsiCapable()
{
    // The Unix-standard kill switch — honour first.
    if (Environment.GetEnvironmentVariable("NO_COLOR") is not null) return false;
    // Piped or redirected — don't poison logs / files with escape bytes.
    if (Console.IsOutputRedirected) return false;
    return true;
}

static string Style(string text, string sgr)
    => AnsiCapable() ? $"\x1b[{sgr}m{text}\x1b[0m" : text;

Console.WriteLine(Style("OK", "32"));
Console.WriteLine(Style("FAIL", "1;31"));
Legacy Windows — SetConsoleMode P/Invoke (pre-1607)
using System;
using System.Runtime.InteropServices;

// Windows 10 1607+ / Windows Terminal / Conhost on 1709+ parse VT by
// default — this block is only needed if you target older builds. On
// non-Windows the P/Invoke just no-ops and falls through.

const int STD_OUTPUT_HANDLE = -11;
const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;

[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);

static void EnableVtOnWindows()
{
    if (!OperatingSystem.IsWindows()) return;
    var handle = GetStdHandle(STD_OUTPUT_HANDLE);
    if (!GetConsoleMode(handle, out var mode)) return;
    SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}

EnableVtOnWindows();
Console.WriteLine("\x1b[32mvt ready\x1b[0m");

Related sequences

Other languages