PrettyConsole 4.1.0
See the version list below for details.
dotnet add package PrettyConsole --version 4.1.0
NuGet\Install-Package PrettyConsole -Version 4.1.0
<PackageReference Include="PrettyConsole" Version="4.1.0" />
<PackageVersion Include="PrettyConsole" Version="4.1.0" />
<PackageReference Include="PrettyConsole" />
paket add PrettyConsole --version 4.1.0
#r "nuget: PrettyConsole, 4.1.0"
#:package PrettyConsole@4.1.0
#addin nuget:?package=PrettyConsole&version=4.1.0
#tool nuget:?package=PrettyConsole&version=4.1.0
PrettyConsole
An abstraction over System.Console that adds new input and output methods, colors and advanced outputs like progress bars and menus. And everything is ansi supported so it works on legacy systems and terminals.
Features
- 🚀 High performance, low allocations and span-first APIs
- 🪶 Very lightweight (no external dependencies)
- ✨ Zero-allocation interpolated string handler for inline colors and formatting
- 💾 Supports legacy ANSI terminals (like Windows 7)
- 🔥 Complete NativeAOT compatibility
- Supports all major platforms (Windows, Linux, Mac)
- ⛓ Uses original output pipes, so that your CLI's can be piped properly
Installation 
dotnet add package PrettyConsole
Usage
Everything starts off with the using statements, I recommend using the Console statically
using static PrettyConsole.Console; // Access to all Console methods
using PrettyConsole; // Access to the Color struct and OutputPipe enum
Interpolated Strings
PrettyConsoleInterpolatedStringHandler lets you stream interpolated text directly to the selected pipe without allocating intermediate strings, while still using the familiar $"..." syntax.
Write($"Hello {Color.Green}world{Color.Default}!");
Write(OutputPipe.Error, $"{Color.Yellow}Warning:{Color.Default} {message}");
if (!TryReadLine(out int choice, $"Pick option {Color.Cyan}1-5{Color.Default}: ")) {
WriteLine($"{Color.Red}Not a number.{Color.Default}");
}
Colors reset automatically at the end of each call. Use Color.Default (or explicit background tuples) when you need to restore colors mid-string.
When interpolating TimeSpan values you can also apply the special :hr format specifier to get compact, human-readable output (ms, ss, mm, hh, or dd depending on the magnitude):
var elapsed = stopwatch.Elapsed;
WriteLine($"Completed in {elapsed:hr}");
ColoredOutput
PrettyConsole uses an equation inspired syntax to colorize text. The syntax is as follows:
WriteLine("Test" * Color.Red / Color.Blue);
i.e TEXT * FOREGROUND / BACKGROUND
Any the 2 colors can be played with just like a real equation, omit the foreground and the default will be used, same goes for the background.
Basic Outputs
The most basic method for outputting is Write, which has multiple overloads. All equivalents exist for WriteLine:
// Usage + overload highlights:
Write($"Interpolated {Color.Blue}string{Color.Default}");
Write(OutputPipe.Error, $"...");
Write(ColoredOutput output, OutputPipe pipe = OutputPipe.Out);
Write(ReadOnlySpan<ColoredOutput> outputs, OutputPipe pipe = OutputPipe.Out);
Write(ReadOnlySpan<char> span, OutputPipe pipe, ConsoleColor foreground);
Write(ReadOnlySpan<char> span, OutputPipe pipe, ConsoleColor foreground, ConsoleColor background);
Write<T>(T value, OutputPipe pipe = OutputPipe.Out) where T : ISpanFormattable;
Write<T>(T value, OutputPipe pipe, ConsoleColor foreground, ConsoleColor background,
ReadOnlySpan<char> format, IFormatProvider? provider);
Overload for WriteLine are available with the same parameters
Basic Inputs
These are the methods for reading user input:
// Examples:
string? ReadLine(); // ReadLine<string>
string? ReadLine(ReadOnlySpan<ColoredOutput>);
string? ReadLine($"Prompt {Color.Green}text{Color.Default}: ");
T? ReadLine<T>(ReadOnlySpan<ColoredOutput>); // T : IParsable<T>
T? ReadLine<T>($"Prompt {Color.Cyan}text{Color.Default}: ");
T ReadLine<T>(ReadOnlySpan<ColoredOutput>, T @default); // @default will be returned if parsing fails
T ReadLine<T>(T @default, $"Prompt {Color.Cyan}text{Color.Default}: ");
bool TryReadLine<T>(ReadOnlySpan<ColoredOutput>, out T?); // T : IParsable<T>
bool TryReadLine<T>(out T?, $"Prompt {Color.Cyan}text{Color.Default}: ");
bool TryReadLine<T>(ReadOnlySpan<ColoredOutput>, T @default, out T); // @default will be returned if parsing fails
bool TryReadLine<T>(out T, T @default, $"Prompt {Color.Cyan}text{Color.Default}: ");
bool TryReadLine<TEnum>(ReadOnlySpan<ColoredOutput>, bool ignoreCase, out TEnum?); // TEnum : struct, Enum
bool TryReadLine<TEnum>(out TEnum, bool ignoreCase, $"Prompt {Color.Cyan}text{Color.Default}: "); // TEnum : struct, Enum
bool TryReadLine<TEnum>(ReadOnlySpan<ColoredOutput>, bool ignoreCase, TEnum @default, out TEnum); // @default will be returned if parsing fails
bool TryReadLine<TEnum>(out TEnum, bool ignoreCase, TEnum @default, $"Prompt {Color.Cyan}text{Color.Default}: ");
I always recommend using TryReadLine instead of ReadLine as you need to maintain less null checks and the result,
especially with @default is much more concise.
Advanced Inputs
These are some special methods for inputs:
// These will wait for the user to press any key
void RequestAnyInput(string message = "Press any key to continue...");
void RequestAnyInput(ReadOnlySpan<ColoredOutput> output);
RequestAnyInput($"Press {Color.Yellow}any key{Color.Default} to continue...");
// These request confirmation by special input from user
bool Confirm(ReadOnlySpan<ColoredOutput> message); // uses the default values ["y", "yes"]
// the default values can also be used by you at Console.DefaultConfirmValues
bool Confirm(ReadOnlySpan<ColoredOutput> message, ReadOnlySpan<string> trueValues, bool emptyIsTrue = true);
bool Confirm($"Deploy to production? ({Color.Green}y{Color.Default}/{Color.Red}n{Color.Default}) ");
bool Confirm(ReadOnlySpan<string> trueValues, bool emptyIsTrue, $"Overwrite existing files? ");
Rendering Controls
To aid in rendering and building your own complex outputs, there are many methods that simplify some processes.
ClearNextLines(int lines, OutputPipe pipe = OutputPipe.Error); // clears the next lines
NewLine(OutputPipe pipe = OutputPipe.Out); // outputs a new line
SetColors(ConsoleColor foreground, ConsoleColor background); // sets the colors of the console output
ResetColors(); // resets the colors of the console output
int GetCurrentLine(); // returns the current line number
GoToLine(int line); // moves the cursor to the specified line
Combining ClearNextLines with GoToLine will enable you to efficiently use the same space in the console for continuous output, such as progress outputting, for some cases there are also built-in methods for this, more on that later.
Advanced Outputs
// This method will essentially write a line, clear it, go back to same position
// This allows a form of text-only progress bar
void OverwriteCurrentLine(ReadOnlySpan<ColoredOutput> output, OutputPipe pipe = OutputPipe.Error);
void Overwrite(Action action, int lines = 1, OutputPipe pipe = OutputPipe.Error);
void Overwrite<TState>(TState state, Action<TState> action, int lines = 1, OutputPipe pipe = OutputPipe.Error)
where TState : allows ref struct;
// This methods will write a character at a time, with a delay between each character
async Task TypeWrite(ColoredOutput output, int delay = TypeWriteDefaultDelay);
async Task TypeWriteLine(ColoredOutput output, int delay = TypeWriteDefaultDelay);
Menus
// This prints an index view of the list, allows the user to select by index
// returns the actual choice that corresponds to the index
string Selection<TList>(ReadOnlySpan<ColoredOutput> title, TList choices) where TList : IList<string> {}
// Same as selection but allows the user to select multiple indexes
// Separated by spaces, and returns an array of the actual choices that correspond to the indexes
string[] MultiSelection<TList>(ReadOnlySpan<ColoredOutput> title, TList choices) where TList : IList<string> {}
// This prints a tree menu of 2 levels, allows the user to select the index
// Of the first and second level and returns the corresponding choices
(string option, string subOption) TreeMenu<TList>(ReadOnlySpan<ColoredOutput> title,
Dictionary<string, TList> menu) where TList : IList<string> {}
// This prints a table with headers, and columns for each list
void Table<TList>(TList headers, ReadOnlySpan<TList> columns) where TList : IList<string> {}
Progress Bars
There are two types of progress bars here, they both are implemented using a class to maintain states.
IndeterminateProgressBar
var prg = new IndeterminateProgressBar(); // this setups the internal states
// Then you need to provide either a Task or Task</T>, the progress bar binds to it and runs until the task completes
await prg.RunAsync(task, "Running...", cancellationToken); // There are also overloads without header
// if the task is not started before being passed to the progress bar, it will be started automatically
// It is even better this way to synchronize the runtime of the progress bar with the task
prg.AnimationSequence = IndeterminateProgressBar.Patterns.CarriageReturn; // customize the animation
ProgressBar
ProgressBar is a more powerful version, but requires a percentage of progress.
// ProgressBar implements IDisposable
var prg = new ProgressBar();
// then on each time the progress percentage is actually changed, you call Update
Update(percentage, ReadOnlySpan<char> status);
// There are also overloads without header, and percentage can be either int or double (0-100).
// Update re-renders on every call, even if the percentage hasn't changed, so you can refresh the status text.
// Also, you can change some of the visual properties of the progress bar after initialization
// by using the properties of the ProgressBar class
prg.ProgressChar = '■'; // Character to fill the progress bar
prg.ForegroundColor = Color.Red; // Color of the empty part
prg.ProgressColor = Color.Blue; // The color of the filled part
// Pass sameLine: false to render the status on a separate line above the bar.
prg.Update(percentage, "Downloading", sameLine: false);
// Need a static, one-off render? Use the helper:
ProgressBar.WriteProgressBar(OutputPipe.Error, percentage, Color.Green, '*');
Multiple Progress Bars with Overwrite
You can combine the static helper with Overwrite to redraw several progress bars inside the same console window—perfect for tracking multiple downloads or tasks:
var downloads = new[] { "Video.mp4", "Archive.zip" };
var progress = new double[downloads.Length];
Overwrite(progress, state => {
for (int i = 0; i < downloads.Length; i++) {
Write(OutputPipe.Error, $"Task {i + 1} ({downloads[i]}): ");
ProgressBar.WriteProgressBar(OutputPipe.Error, state[i], Color.Cyan);
NewLine(OutputPipe.Error);
}
}, lines: downloads.Length, pipe: OutputPipe.Error);
Update the progress array elsewhere and call Overwrite again to refresh the stacked bars without leaving artifacts.
Pipes
Console wraps over System.Console and uses its In, Out, and Error streams. Since the names of the classes are identical, combining them in usage is somewhat painful as the compiler doesn't know which overloads to use. To aid in most cases,
Console exposes those streams as static properties, and you can use them directly.
In rare cases, you will need something that there is in System.Console but not in Console, such as ReadKey, or SetCursorPosition, some events or otherwise, then you can simply call System.Console, this added verbosity is a worthy trade-off.
Contributing
This project uses an MIT license, if you want to contribute, you can do so by forking the repository and creating a pull request.
If you have feature requests or bug reports, please create an issue.
Contact
For bug reports, feature requests or offers of support/sponsorship contact dusrdev@gmail.com
This project is proudly made in Israel 🇮🇱 for the benefit of mankind.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 5.3.0 | 152 | 12/4/2025 | |
| 5.2.0 | 101 | 11/29/2025 | |
| 5.1.0 | 188 | 11/24/2025 | |
| 5.0.0 | 163 | 11/15/2025 | |
| 4.1.0 | 144 | 10/25/2025 | |
| 4.0.0 | 174 | 10/21/2025 | |
| 3.1.0 | 456 | 11/14/2024 | |
| 3.0.0 | 3,717 | 10/21/2024 | |
| 3.0.0-rc.2 | 98 | 10/16/2024 | |
| 3.0.0-rc.1 | 91 | 10/11/2024 | |
| 2.1.1 | 238 | 8/25/2024 | |
| 2.1.0 | 235 | 8/23/2024 | |
| 2.0.0 | 292 | 3/2/2024 | |
| 1.6.1 | 378 | 6/30/2023 | |
| 1.6.0 | 336 | 6/26/2023 | |
| 1.5.2 | 384 | 4/14/2023 | |
| 1.5.1 | 498 | 12/21/2022 | |
| 1.5.0 | 546 | 12/4/2022 | |
| 1.4.0 | 637 | 8/4/2022 | |
| 1.3.0 | 597 | 7/14/2022 | |
| 1.2.0 | 586 | 7/5/2022 | |
| 1.1.0 | 597 | 6/18/2022 | |
| 1.0.2 | 632 | 6/14/2022 | |
| 1.0.1 | 646 | 6/14/2022 | |
| 1.0.0 | 653 | 6/12/2022 |
- ProgressBar.ForegroundColor docs were fixed (they previously were the same as
ProgressColor) which is invalid.
- ProgressBar in all variations now shows progress as a round number suffixed by %.
- ProgressBar no longer tracks if the percentage is changed, being that the numbers
are round, percentage could progress or status needs to be re-written while it stays the
same when rounded.
- ProgressBar.Update overloads now include an optional parameter "sameLine" which
configures whether to render the progress bar at the same of the status. It is set to
"true" by default to keep current behavior.
- ProgressBar now includes a static method "WriteProgressBar" which renders a static
progress bar with the set parameters, it can be used in conjunction with "Overwrite" to
create multi-progress-bars UI.
- Methods that overwrite lines, now have a note in the remarks to clear the used lines
after the last call, to prevent artifacts.