You want to add charts to your Blazor app. Maybe it is a dashboard for your SaaS product, an admin panel with analytics, or a reporting page for your enterprise app. But where do you start?
This tutorial covers everything — from rendering a raw SVG bar chart in Blazor with zero dependencies, to building a fully interactive multi-series line chart with tooltips, crosshairs, and real-time data. By the end, you will know exactly how Blazor chart components work and how to ship them to production.
Option 1: Build a Chart from Scratch with SVG
Before reaching for a library, it helps to understand how Blazor charts work under the hood. Every chart is ultimately SVG or Canvas. Here is a minimal bar chart in pure Blazor — no libraries, no JavaScript:
@page "/my-chart"
<svg width="400" height="200" viewBox="0 0 400 200">
@foreach (var (label, value, index) in data.Select((d, i) => (d.Label, d.Value, i)))
{
var barHeight = value * 2;
var x = index * 80 + 20;
var y = 200 - barHeight;
<rect x="@x" y="@y" width="60" height="@barHeight"
fill="#8b5cf6" rx="4" />
<text x="@(x + 30)" y="195" text-anchor="middle"
fill="#9490b3" font-size="12">@label</text>
}
</svg>
@code {
private List<(string Label, double Value)> data = new()
{
("Jan", 45), ("Feb", 72), ("Mar", 58), ("Apr", 88), ("May", 64)
};
}
This renders five purple bars with labels. It works. But it has serious limitations:
- No tooltips — users cannot hover to see exact values
- No responsiveness — fixed width, no resize handling
- No accessibility — screen readers cannot interpret the data
- No axes — no gridlines, tick marks, or axis labels
- No animation — data changes pop in without transitions
- No interactivity — no click events, no selection, no zoom
Building all of that from scratch is hundreds of hours of work. That is why chart libraries exist.
Option 2: Use a Blazor Chart Library
A good chart library handles responsive layout, accessibility, tooltips, animations, axis formatting, and cross-browser rendering so you can focus on your data. Here is how the major Blazor chart libraries compare in 2026:
| Feature | Arcadia Controls | MudBlazor | Radzen | ChartJs.Blazor |
|---|---|---|---|---|
| Rendering | Pure SVG (C#) | SVG (JS interop) | SVG (JS interop) | Canvas (JS) |
| JS required | No | Yes | Yes | Yes (Chart.js) |
| Free chart types | 4 | 6 | 8 | 8 |
| Paid chart types | 16 total | N/A | 12+ | N/A |
| .NET 10 | Yes | Yes | Yes | No |
| Auto render mode | Yes | Limited | Yes | No |
| Accessibility | WCAG 2.1 AA | Partial | Partial | None |
| Dashboard widgets | 7 | None | Some | None |
| Last updated | March 2026 | March 2026 | February 2026 | 2023 |
For this tutorial, we will use Arcadia Controls because it renders pure SVG from C# (no JavaScript to configure), supports every Blazor render mode, and has a free Community Edition.
Tutorial — Create a Line Chart with Arcadia Controls
Step 1: Create a new Blazor project
If you already have a Blazor project, skip to Step 2. Otherwise:
dotnet new blazor -n ChartDemo --interactivity Auto
cd ChartDemo
This creates a .NET 9+ Blazor project with Auto interactivity (server-side rendering with WebAssembly takeover).
Step 2: Install the Arcadia Charts package
dotnet add package Arcadia.Charts
The package targets .NET 5 through .NET 10, so it works regardless of your project’s target framework.
Step 3: Add the stylesheet
Open your App.razor (or _Host.cshtml for older Blazor Server projects) and add the CSS link in the <head>:
<link rel="stylesheet" href="_content/Arcadia.Charts/css/arcadia-charts.css" />
Step 4: Create a page with a basic line chart
Arcadia charts are strongly-typed generic components. You give the chart your data model, an XField accessor for the X-axis value, and a list of Series that say how to extract Y-values. Create a new file Components/Pages/Dashboard.razor:
@page "/dashboard"
@using Arcadia.Charts.Core
@using Arcadia.Charts.Components.Charts
<h1>Sales Dashboard</h1>
<ArcadiaLineChart TItem="MonthRevenue"
Data="@revenue"
XField="@(d => (object)d.Month)"
Series="@series"
Width="700"
Height="400"
XAxisTitle="Month"
YAxisTitle="Revenue ($k)"
ShowGrid="true" />
@code {
record MonthRevenue(string Month, double Revenue);
private List<MonthRevenue> revenue = new()
{
new("Jan", 32.1), new("Feb", 41.5), new("Mar", 38.7),
new("Apr", 52.3), new("May", 61.8), new("Jun", 58.2),
new("Jul", 67.4), new("Aug", 72.1), new("Sep", 69.8),
new("Oct", 78.5), new("Nov", 85.3), new("Dec", 91.7),
};
private List<SeriesConfig<MonthRevenue>> series = new()
{
new() { Name = "Revenue", Field = d => d.Revenue, Color = "#8b5cf6" },
};
}
Navigate to /dashboard and you will see a line chart with monthly revenue data. The chart renders as SVG — inspect the DOM and you will see <svg>, <path>, <circle>, and <text> elements. No <canvas>, no JavaScript runtime.
Step 5: Add multiple series
Real dashboards compare data. The chart already takes a List<SeriesConfig<T>>, so adding a second line is one entry in series:
<ArcadiaLineChart TItem="YearOverYear"
Data="@data"
XField="@(d => (object)d.Month)"
Series="@series"
Width="700"
Height="400"
XAxisTitle="Month"
YAxisTitle="Revenue ($k)"
ShowGrid="true"
ShowLegend="true" />
@code {
record YearOverYear(string Month, double Y2025, double Y2026);
private List<YearOverYear> data = new()
{
new("Jan", 32.1, 38.5), new("Feb", 41.5, 49.2),
new("Mar", 38.7, 45.1), new("Apr", 52.3, 59.7),
new("May", 61.8, 70.3), new("Jun", 58.2, 65.8),
new("Jul", 67.4, 74.2), new("Aug", 72.1, 81.5),
new("Sep", 69.8, 77.3),
};
private List<SeriesConfig<YearOverYear>> series = new()
{
new() { Name = "2025 Revenue", Field = d => d.Y2025, Color = "#8b5cf6" },
new() { Name = "2026 Revenue", Field = d => d.Y2026, Color = "#ec4899" },
};
}
The ShowLegend="true" parameter adds a color-coded legend so users can distinguish the series.
Step 6: Add area fill and smooth curves
Curve shape and area fill are per-series. Set CurveType = "smooth" for spline interpolation and ShowArea = true for the translucent fill beneath each line:
private List<SeriesConfig<YearOverYear>> series = new()
{
new() { Name = "2025 Revenue", Field = d => d.Y2025, Color = "#8b5cf6",
CurveType = "smooth", ShowArea = true, AreaOpacity = 0.15 },
new() { Name = "2026 Revenue", Field = d => d.Y2026, Color = "#ec4899",
CurveType = "smooth", ShowArea = true, AreaOpacity = 0.15 },
};
CurveType accepts "linear" (default), "smooth" (Catmull-Rom spline), or "step".
Step 7: Tooltips and crosshair
Tooltips are on by default for data points. To add a vertical crosshair that tracks the cursor and helps align values across series, set ShowCrosshair="true" on the chart:
<ArcadiaLineChart TItem="YearOverYear"
Data="@data"
XField="@(d => (object)d.Month)"
Series="@series"
Width="700"
Height="400"
XAxisTitle="Month"
YAxisTitle="Revenue ($k)"
ShowGrid="true"
ShowLegend="true"
ShowCrosshair="true"
YAxisFormatString="$#,##0.0k" />
YAxisFormatString controls how the Y-axis ticks (and the values inside default tooltips) are formatted.
Adding Interactivity
Click events
OnPointClick is a strongly-typed EventCallback<PointClickEventArgs<T>>. The Razor source generator sometimes has trouble inferring the type argument when binding a typed handler with a method group, so the safest pattern is the explicit factory shown below:
<ArcadiaLineChart TItem="MonthRevenue"
Data="@revenue"
XField="@(d => (object)d.Month)"
Series="@series"
Width="700"
Height="400"
OnPointClick="@(EventCallback.Factory.Create<PointClickEventArgs<MonthRevenue>>(this, HandlePointClick))" />
<p>Selected: @selectedPoint</p>
@code {
private string selectedPoint = "Click a data point";
private void HandlePointClick(PointClickEventArgs<MonthRevenue> args)
{
selectedPoint = $"{args.SeriesName}: {args.Value:C1} ({args.Item.Month})";
}
}
PointClickEventArgs<T> carries the clicked item, its data index, the series name, and the Y-value.
Annotations and reference lines
Annotations are a parameter, not a child element. Pass a List<ChartAnnotation>:
<ArcadiaLineChart TItem="MonthRevenue"
Data="@revenue"
XField="@(d => (object)d.Month)"
Series="@series"
Annotations="@annotations"
Width="700"
Height="400"
ShowGrid="true" />
@code {
private List<ChartAnnotation> annotations = new()
{
new ChartAnnotation
{
Type = ChartAnnotationType.HorizontalLine,
Value = 70,
Color = "#f59e0b",
Label = "Target",
StrokeDash = "6,4",
},
};
}
A dashed horizontal line at 70k is useful for KPI tracking, SLA thresholds, and budget targets.
Dynamic data updates
Charts automatically re-render when their parameters change. For real-time / streaming data, use an ObservableCollection<T> — the chart subscribes to INotifyCollectionChanged and re-renders without manual StateHasChanged calls:
@using System.Collections.ObjectModel
<ArcadiaLineChart TItem="Tick"
Data="@liveData"
XField="@(d => (object)d.Time)"
Series="@series"
Width="700"
Height="400" />
<button @onclick="AddTick">Add Point</button>
@code {
record Tick(DateTime Time, double Value);
private ObservableCollection<Tick> liveData = new();
private List<SeriesConfig<Tick>> series = new()
{
new() { Name = "Live", Field = d => d.Value, Color = "#10b981", CurveType = "smooth" },
};
protected override void OnInitialized()
{
var t = DateTime.Now;
for (int i = 0; i < 5; i++) liveData.Add(new Tick(t.AddSeconds(i), 10 + i * 3));
}
private void AddTick()
{
var last = liveData[^1];
var rng = new Random();
liveData.Add(new Tick(last.Time.AddSeconds(1), last.Value + rng.Next(-5, 8)));
}
}
Creating Other Chart Types
Bar charts use the same Data + XField + Series pattern as line charts:
<ArcadiaBarChart TItem="QuarterRow"
Data="@quarters"
XField="@(d => (object)d.Region)"
Series="@barSeries"
Width="600"
Height="350"
ShowGrid="true" />
@code {
record QuarterRow(string Region, double Q1, double Q2);
private List<QuarterRow> quarters = new()
{
new("NA", 85, 92), new("EU", 72, 81),
new("APAC", 91, 88), new("LATAM", 68, 75), new("MEA", 77, 83),
};
private List<SeriesConfig<QuarterRow>> barSeries = new()
{
new() { Name = "Q1", Field = d => d.Q1, Color = "#8b5cf6" },
new() { Name = "Q2", Field = d => d.Q2, Color = "#ec4899" },
};
}
Pie charts have a simpler shape — one NameField and one ValueField:
<ArcadiaPieChart TItem="DeviceShare"
Data="@devices"
NameField="@(d => d.Device)"
ValueField="@(d => d.Share)"
Width="400"
Height="400"
ShowLabels="true" />
@code {
record DeviceShare(string Device, double Share);
private List<DeviceShare> devices = new()
{
new("Desktop", 58.3), new("Mobile", 31.2), new("Tablet", 10.5),
};
}
Going Further
This tutorial covered the fundamentals of creating Blazor charts. Here is where to go next:
More chart types:
- Bar Chart docs — grouped, stacked, horizontal
- Pie Chart docs — slices, labels, custom colors
- Scatter Chart docs — point clouds, bubble sizing
- All chart types overview — complete list with examples
Dashboard widgets:
- KPI Card — single-value metrics with trend arrows
- Gauge — radial and linear gauges
- Sparkline — inline mini charts
Advanced features:
- Streaming data — real-time charts with WebSocket feeds
- Theming — customize colors, fonts, and dark/light modes
- Export — save charts as PNG or SVG
Tools:
- Chart Playground — interactive builder to design charts visually
- MCP Server — AI-assisted chart code generation
- NuGet Package — latest release
Get Started Now
Install the free Community Edition and have your first chart rendering in under a minute:
dotnet add package Arcadia.Charts
The Community Edition includes Line, Bar, Pie, and Scatter charts — enough for most dashboards. When you need advanced visualizations like Treemaps, Heatmaps, or Candlestick charts, Pro starts at $299/developer/year.
Every chart in this tutorial works in Blazor Server, WebAssembly, and Auto render modes without any code changes. No JavaScript configuration required.