diff --git a/.gitignore b/.gitignore
index 537737dd..0f148ac9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,10 @@
dotnet-install.sh*
*.lock.json
+# Benchmarks
+**/BenchmarkDotNet.Artifacts/**/*.*
+!**/BenchmarkDotNet.Artifacts/**/*.md
+
# Visual Studio
.vs/
.vscode/
diff --git a/src/Benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.AnsiBenchmarks-report-github.md b/src/Benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.AnsiBenchmarks-report-github.md
new file mode 100644
index 00000000..4222ac88
--- /dev/null
+++ b/src/Benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.AnsiBenchmarks-report-github.md
@@ -0,0 +1,17 @@
+```
+
+BenchmarkDotNet v0.15.8, macOS Tahoe 26.2 (25C56) [Darwin 25.2.0]
+Apple M3 Pro, 1 CPU, 11 logical and 11 physical cores
+.NET SDK 10.0.101
+ [Host] : .NET 10.0.1 (10.0.1, 10.0.125.57005), Arm64 RyuJIT armv8.0-a
+ .NET 10.0 : .NET 10.0.1 (10.0.1, 10.0.125.57005), Arm64 RyuJIT armv8.0-a
+ .NET 8.0 : .NET 8.0.17 (8.0.17, 8.0.1725.26602), Arm64 RyuJIT armv8.0-a
+ .NET 9.0 : .NET 9.0.10 (9.0.10, 9.0.1025.47515), Arm64 RyuJIT armv8.0-a
+
+
+```
+| Method | Job | Runtime | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
+|----------------- |---------- |---------- |---------:|---------:|---------:|-------:|-------:|----------:|
+| RenderableToAnsi | .NET 10.0 | .NET 10.0 | 12.33 μs | 0.054 μs | 0.045 μs | 5.3558 | 0.1831 | 43.87 KB |
+| RenderableToAnsi | .NET 8.0 | .NET 8.0 | 14.12 μs | 0.100 μs | 0.094 μs | 5.7068 | 0.1831 | 46.73 KB |
+| RenderableToAnsi | .NET 9.0 | .NET 9.0 | 14.12 μs | 0.052 μs | 0.049 μs | 5.6458 | 0.1984 | 46.17 KB |
diff --git a/src/Benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.RenderBenchmarks-report-github.md b/src/Benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.RenderBenchmarks-report-github.md
new file mode 100644
index 00000000..01cd4671
--- /dev/null
+++ b/src/Benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.RenderBenchmarks-report-github.md
@@ -0,0 +1,17 @@
+```
+
+BenchmarkDotNet v0.15.8, macOS Tahoe 26.2 (25C56) [Darwin 25.2.0]
+Apple M3 Pro, 1 CPU, 11 logical and 11 physical cores
+.NET SDK 10.0.101
+ [Host] : .NET 10.0.1 (10.0.1, 10.0.125.57005), Arm64 RyuJIT armv8.0-a
+ .NET 10.0 : .NET 10.0.1 (10.0.1, 10.0.125.57005), Arm64 RyuJIT armv8.0-a
+ .NET 8.0 : .NET 8.0.17 (8.0.17, 8.0.1725.26602), Arm64 RyuJIT armv8.0-a
+ .NET 9.0 : .NET 9.0.10 (9.0.10, 9.0.1025.47515), Arm64 RyuJIT armv8.0-a
+
+
+```
+| Method | Job | Runtime | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
+|------- |---------- |---------- |---------:|---------:|---------:|-------:|-------:|----------:|
+| Render | .NET 10.0 | .NET 10.0 | 12.45 μs | 0.130 μs | 0.101 μs | 5.4169 | 0.0916 | 44.34 KB |
+| Render | .NET 8.0 | .NET 8.0 | 21.74 μs | 0.033 μs | 0.031 μs | 5.7678 | 0.0916 | 47.2 KB |
+| Render | .NET 9.0 | .NET 9.0 | 14.37 μs | 0.070 μs | 0.065 μs | 5.7068 | 0.0916 | 46.64 KB |
diff --git a/src/Benchmarks/Benchmarks.csproj b/src/Benchmarks/Benchmarks.csproj
new file mode 100644
index 00000000..cc72c0ba
--- /dev/null
+++ b/src/Benchmarks/Benchmarks.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net10.0;net9.0;net8.0
+ true
+ true
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Benchmarks/Program.cs b/src/Benchmarks/Program.cs
new file mode 100644
index 00000000..be2baa75
--- /dev/null
+++ b/src/Benchmarks/Program.cs
@@ -0,0 +1,84 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
+using BenchmarkDotNet.Running;
+using Spectre.Console;
+using Spectre.Console.Rendering;
+
+namespace Benchmarks;
+
+public static class Program
+{
+ public static int Main(string[] args)
+ {
+ BenchmarkRunner.Run(typeof(Program).Assembly);
+ return 0;
+ }
+}
+
+[MemoryDiagnoser]
+[SimpleJob(RuntimeMoniker.Net80)]
+[SimpleJob(RuntimeMoniker.Net90)]
+[SimpleJob(RuntimeMoniker.Net10_0)]
+public class RenderBenchmarks
+{
+ private IAnsiConsole _console = null!;
+ private IRenderable _renderable = null!;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ _console =
+ AnsiConsole.Create(new AnsiConsoleSettings
+ {
+ Ansi = AnsiSupport.Detect,
+ ColorSystem = ColorSystemSupport.TrueColor,
+ Out = new AnsiConsoleOutput(new StringWriter()),
+ Interactive = InteractionSupport.No,
+ EnvironmentVariables = null
+ });
+
+ _renderable = new Table()
+ .AddColumns("Foo", "Bar")
+ .AddRow(new Text("1"), new Table()
+ .AddColumns("Baz", "Qux")
+ .AddRow("1", "2")
+ .AddRow("3", "4"));
+ }
+
+ [Benchmark]
+ public void Render() => _console.Write(_renderable);
+}
+
+[MemoryDiagnoser]
+[SimpleJob(RuntimeMoniker.Net80)]
+[SimpleJob(RuntimeMoniker.Net90)]
+[SimpleJob(RuntimeMoniker.Net10_0)]
+public class AnsiBenchmarks
+{
+ private IAnsiConsole _console = null!;
+ private IRenderable _renderable = null!;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ _console =
+ AnsiConsole.Create(new AnsiConsoleSettings
+ {
+ Ansi = AnsiSupport.Detect,
+ ColorSystem = ColorSystemSupport.TrueColor,
+ Out = new AnsiConsoleOutput(new StringWriter()),
+ Interactive = InteractionSupport.No,
+ EnvironmentVariables = null
+ });
+
+ _renderable = new Table()
+ .AddColumns("Foo", "Bar")
+ .AddRow(new Text("1"), new Table()
+ .AddColumns("Baz", "Qux")
+ .AddRow("1", "2")
+ .AddRow("3", "4"));
+ }
+
+ [Benchmark]
+ public void RenderableToAnsi() => _console.ToAnsi(_renderable);
+}
\ No newline at end of file
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index df44f841..e8ebd4cc 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -3,6 +3,7 @@
true
+
diff --git a/src/Spectre.Console.slnx b/src/Spectre.Console.slnx
index ff12edc6..c43e9401 100644
--- a/src/Spectre.Console.slnx
+++ b/src/Spectre.Console.slnx
@@ -19,6 +19,7 @@
+