Introduction
Learn how to use Spectre.Console Tree widget.
The Tree widget visualizes hierarchical data structures with parent-child relationships using Unicode tree characters.
💡 Tree API
Code samples
Are fully documented.
Base example
public static void Example1() { // Create a tree with a root node var tree = new Tree("[deeppink3 bold]root node[/]") .Guide(TreeGuide.Line); // Add child node to the root node TreeNode types = tree.AddNode("[yellow]Types[/] [dim]Child 1[/]"); // Add grandchild nodes to the child node types.AddNode("[green]String[/] [dim]Grandchild 1.1[/]"); types.AddNode("[green]Int32[/] [dim]Grandchild 1.2[/]"); types.AddNode("[green]Boolean[/] [dim]Grandchild 1.3[/]"); // Add child node to the root node TreeNode collection = tree.AddNode("[blue]Collections[/] [dim]Child 1[/]"); // Add grandchild nodes to the child node collection.AddNode("[green]List<String>[/] [dim]Grandchild 1.1[/]"); collection.AddNode("[green]Dictionary<String,Int32>[/] [dim]Grandchild 1.2[/]"); AnsiConsole.Write(tree); } - First create a new Tree with a colorized label (Initializes a new instance of the Tree class.) and specify line type via Guide (Gets or sets the tree guide lines).
- Create child nodes off the tree
- Create child nodes (Grandchild)
- Use AnsiConsole.Write(tree) to display the tree
Using a Dictionary
This example uses a Dictionary<string, string[]>
public static void Example2() { var fileSystem = new Dictionary<string, string[]> { ["Configuration"] = ["appsettings.json", "secrets.json"], ["Source Code"] = ["Main.cs", "Helper.cs", "Utilities.cs"], ["Documentation"] = ["README.md", "CHANGELOG.md"], }; var tree = new Tree("[green bold]Application[/]"); foreach (var (folder, files) in fileSystem) { var folderNode = tree.AddNode($"[yellow]{folder}[/]"); foreach (var file in files) { folderNode.AddNode($"[grey]{file}[/]"); } } AnsiConsole.Write(tree); } - Dictionary keys represent child nodes of the tree node
- Dictionary values represent Grandchild nodes
Using JSON and Grouping
The basics apply as per above, a tree is created followed by child nodes by IEnumerable<IGrouping<string, Book>>
Model
public class Book { public int Id { get; set; } public string Title { get; set; } public decimal? Price { get; set; } } internal class BookOperations { public static void BooksGroupings() { SpectreConsoleHelpers.PrintPink(); List<Book> books = JsonSerializer.Deserialize<List<Book>>(Json())!; IEnumerable<IGrouping<string, Book>> grouped = GroupBooksByPriceRange(books); var tree = new Tree("[bold yellow]Books by Price Range[/]"); foreach (var (price, booksGroup) in grouped.Select(group => (group.Key, group))) { var priceNode = tree.AddNode($"[bold cyan]{Markup.Escape(price)}[/]"); foreach (var book in booksGroup) { priceNode.AddNode( $"[green]Id:[/] {book.Id}, " + $"[green]Title:[/] {Markup.Escape(book.Title)}, " + $"[green]Price:[/] {book.Price:C}"); } } AnsiConsole.Write(tree); } private static string Json() { var json = /* lang=json*/ """ [ { "Id": 1, "Title": "Learn EF Core", "Price": 19.0 }, { "Id": 2, "Title": "C# Basics", "Price": 18.0 }, { "Id": 3, "Title": "ASP.NET Core advance", "Price": 30.0 }, { "Id": 4, "Title": "VB.NET To C#", "Price": 9.0 }, { "Id": 5, "Title": "Basic Azure", "Price": 59.0 } ] """; return json; } private static IEnumerable<IGrouping<string, Book>> GroupBooksByPriceRange(List<Book> books) => books.GroupBy(b => b switch { { Price: < 10 } => "Under $10", { Price: >= 10 and < 20 } => "$10 to $19", { Price: >= 20 and < 30 } => "$20 to $29", { Price: >= 30 } => "$30 and above", _ => "Unknown" }); } EF Core example
This example uses one table to show relations for Employees and Managers.
internal class EmployeeOperations { public static void ReportsToManager() { PrintPink(); using var context = new Context(); var table = CreateViewTable(); List<Employees> employees = [.. context.Employees]; List<IGrouping<int?, Employees>> groupedData = employees .Where(employee => employee.ReportsTo.HasValue) .OrderBy(employee => employee.LastName) .GroupBy(employee => employee.ReportsTo) .ToList(); List<Manager> managers = []; foreach (var group in groupedData) { Manager manager = new() { Employee = employees.Find(employee => employee.EmployeeID == group.Key.Value) }; foreach (Employees groupedItem in group) { manager.Workers.Add(groupedItem); } managers.Add(manager); } managers = managers .OrderBy(employee => employee.Employee.LastName) .ToList(); var root = new Tree("[white][B]Employees[/][/]"); foreach (Manager manager in managers) { var currentNode = root.AddNode($"[Chartreuse1]{manager.Employee.FullName}[/] [Gray37](Manager)[/]"); foreach (var worker in manager.Workers) { /* * Here a worker can also be a manager, so we check if the worker is also listed as a manager * in our managers list. If they are, we format their name differently to indicate that * they are both a worker and a manager. */ if (managers.Any(m => m.Employee.EmployeeID == worker.EmployeeID)) { currentNode.AddNode($"{worker.FullName} <a href="Manager">Gray37</a>[/]"); } else { currentNode.AddNode(worker.FullName); } } } AnsiConsole.Write(root); Console.WriteLine(); } private static Table CreateViewTable() => new Table() .Border(TableBorder.Square) .BorderColor(Color.Grey100) .Alignment(Justify.Center) .Title("~[white on blue][B]Employees[/][/]~") .AddColumn(new TableColumn("[u]Manager[/]")) .AddColumn(new TableColumn("[u]Workers[/]")); } Recursive directory tree with Collapse
Iterates a directory and collapses specific subfolders (obj and bin).
public static class DirectoryTree { public static void Display(string path) { if (!Directory.Exists(path)) { AnsiConsole.MarkupLine("[red]Directory does not exist.[/]"); return; } DirectoryInfo root = new(path); Tree tree = new($"[bold yellow]{Markup.Escape(root.FullName)}[/]"); AddDirectory(root, tree); AnsiConsole.Write(tree); } private static void AddDirectory(DirectoryInfo directory, IHasTreeNodes parent) { bool isBinObj = directory.Name.Equals("obj", StringComparison.OrdinalIgnoreCase) || directory.Name.Equals("bin", StringComparison.OrdinalIgnoreCase); try { foreach (DirectoryInfo subdirectory in directory.GetDirectories().OrderBy(d => d.Name)) { TreeNode node = parent.AddNode($"[blue]📁 {Markup.Escape(subdirectory.Name)}[/]"); AddDirectory(subdirectory, node); if (isBinObj) { node.Collapse(); } } if (!isBinObj) { foreach (FileInfo file in directory.GetFiles().OrderBy(f => f.Name)) { parent.AddNode($"[green]📄 {Markup.Escape(file.Name)}[/]"); } } } catch (UnauthorizedAccessException uae) { parent.AddNode("[red]Access denied[/]"); Log.Error(uae, "Access denied to directory: {Directory}", directory.FullName); } catch (IOException ex) { parent.AddNode($"[red]{Markup.Escape(ex.Message)}[/]"); Log.Error(ex, "IO exception occurred while accessing directory: {Directory}", directory.FullName); } } } Main Source code EF Core Source code
Summary
The provided code samples make it easy to create trees.






