A .NET wrapper for D2, the modern diagram scripting language that turns text to diagrams.
- Zero Configuration - Works out of the box with sensible defaults
- High Performance - Built-in worker pool handles concurrent requests efficiently
- Full D2 Support - All D2 features: layouts, themes, sketch mode, and more
- Async/Await - Task-based async rendering with cancellation support
- Cross-Platform - Windows, macOS, and Linux
- Production Ready - Battle-tested with comprehensive error handling
dotnet add package D2Sharpusing D2Sharp;
// Create a renderer (uses 3 worker processes by default)
using var renderer = new D2Renderer();
// Render a diagram
var result = await renderer.RenderDiagramAsync("A -> B -> C");
if (result.IsSuccess)
{
File.WriteAllText("diagram.svg", result.Svg);
}
else
{
Console.WriteLine($"Error: {result.Error?.Message}");
}That's it! D2Sharp handles all the complexity for you.
using var renderer = new D2Renderer();
var script = @"
server -> database: queries
database -> cache: reads
";
var result = await renderer.RenderDiagramAsync(script);var result = await renderer.RenderDiagramAsync("A -> "); // Invalid
if (!result.IsSuccess)
{
var error = result.Error;
Console.WriteLine($"Line {error.LineNumber}: {error.Message}");
Console.WriteLine($" {error.LineContent}");
}var result = renderer.RenderDiagram("A -> B"); // Blocks until complete// With cancellation token
var cts = new CancellationTokenSource();
var result = await renderer.RenderDiagramAsync(script, cancellationToken: cts.Token);
// The renderer handles timeouts automatically - diagrams that take too long
// will be cancelled and return an error resultvar options = new RenderOptions
{
ThemeId = 1 // Cool classics theme
};
var result = await renderer.RenderDiagramAsync("server -> database", options);300+ themes available: View all D2 themes →
var options = new RenderOptions
{
Layout = LayoutEngine.Elk // or LayoutEngine.Dagre (default)
};- Dagre - Fast, clean layouts (default)
- ELK - More complex layouts with additional features
var options = new RenderOptions
{
Sketch = true // Hand-drawn style
};var options = new RenderOptions
{
Pad = 50, // Padding (default: 100)
Scale = 0.5, // Scale factor
Center = true // Center in viewbox
};var options = new RenderOptions
{
Layout = LayoutEngine.Elk,
ThemeId = 1,
Sketch = true,
Pad = 75
};
var result = await renderer.RenderDiagramAsync(complexDiagram, options);// For high-concurrency scenarios
using var renderer = new D2Renderer(workerCount: 15);The default (3 workers) is optimal for most use cases.
// In Program.cs - Basic registration (uses 10 workers by default)
builder.Services.AddD2Sharp();
// With fluent configuration
builder.Services.AddD2Sharp(d2 => d2
.UseProcessPool(pool => pool.WithWorkerCount(15))
.ConfigureCaching(cache => cache.Enabled = true)
.ConfigureTelemetry(telemetry => telemetry.EnableMetrics = true));
// Or use direct mode for CLI tools (no process pool)
builder.Services.AddD2Sharp(d2 => d2.UseDirect());
// In your controller/endpoint
public class DiagramController : ControllerBase
{
private readonly D2Renderer _renderer;
public DiagramController(D2Renderer renderer)
{
_renderer = renderer;
}
[HttpPost]
public async Task<IActionResult> Render([FromBody] string script)
{
var result = await _renderer.RenderDiagramAsync(script);
return result.IsSuccess
? Content(result.Svg, "image/svg+xml")
: BadRequest(result.Error);
}
}The fluent builder API provides several configuration options:
builder.Services.AddD2Sharp(d2 => d2
.UseProcessPool(pool => pool.WithWorkerCount(15))); // Customize worker countbuilder.Services.AddD2Sharp(d2 => d2
.ConfigureCaching(cache =>
{
cache.Enabled = true;
cache.MaxSize = 500;
cache.Expiration = TimeSpan.FromMinutes(30);
}));builder.Services.AddD2Sharp(d2 => d2
.ConfigureTelemetry(telemetry =>
{
telemetry.EnableTracing = true;
telemetry.EnableMetrics = true;
telemetry.EnableDiagnosticIds = true;
}));builder.Services.AddD2Sharp(d2 => d2
.UseDirect()
.ConfigureConcurrency(concurrency =>
{
concurrency.MaxConcurrentRenders = 5;
}));builder.Services.AddD2Sharp(d2 => d2
.UseProcessPool(pool => pool.WithWorkerCount(20))
.ConfigureCaching(cache => cache.Enabled = true)
.ConfigureTelemetry(telemetry => telemetry.EnableMetrics = true));For command-line tools or single-threaded applications where you don't need worker processes:
using var renderer = D2Renderer.CreateDirect();
var result = await renderer.RenderDiagramAsync(script);Note: Direct mode is not recommended for web applications with concurrent requests.
# Shapes and connections
server -> client: HTTPS
# Containers
network: {
router
switch
router -> switch
}
# Styling
server.style.fill: "#4CAF50"
client.shape: person
# Direction
direction: rightD2Sharp is optimized for production workloads:
- Concurrent requests: Efficiently handled via worker pool
- Memory efficient: Automatic cleanup and minimal allocations
- Fast: Simple diagrams render in ~30-50ms
Typical performance (on Apple Silicon):
- Simple diagrams: 30-50ms
- Complex diagrams: 100-200ms
- Very complex: 300-500ms
- .NET 8.0 SDK
- Go 1.22+
- GCC
Check dependencies:
# Windows
.\depcheck.ps1
# Unix/Linux/macOS
./depcheck.shdotnet buildsrc/D2Sharp- Main librarysrc/D2Sharp.Abstractions- Shared interfaces and typessrc/D2Sharp.Worker- Worker process for process isolationexamples/D2Sharp.Web- Web demo applicationtests/D2Sharp.Tests- Unit teststests/D2Sharp.IntegrationTests- Integration tests
- Contributing - Contribution guidelines
MIT License - see LICENSE.txt for details.
- D2 - The diagram scripting language
- Built with .NET 8.0