data-display
chart
Chart
Zero-dependency SVG bar / line / area.
$
Component docs
ndui add chart
Three chart types, one component, no dependencies
bar
Bar — monthly revenue
line
Line — daily active users
area
Area — storage used (30d)
Source — one line per chart
<Chart Type="bar" Data="@Model.Revenue" Width="240" Height="64" AriaLabel="Monthly revenue" />
<Chart Type="line" Data="@Model.Dau" Width="240" Height="64" AriaLabel="Daily active users" />
<Chart Type="area" Data="@Model.Storage" Width="240" Height="64" AriaLabel="Storage used (30d)" />
Use-case 1 — KPI card with sparkline
Revenue
$42.8K
+12.4%
Active users
2,480
+3.1%
Storage
4.2 TB
+680 GB
Churn
1.8%
-0.4%
Source — PageModel + Razor
public sealed class IndexModel(IDbConnection db) : PageModel
{
public IReadOnlyList<double> Revenue { get; private set; } = [];
public IReadOnlyList<double> Dau { get; private set; } = [];
public IReadOnlyList<double> Storage { get; private set; } = [];
public async Task OnGetAsync(CancellationToken ct)
{
Revenue = (await db.QueryAsync<double>(@"
SELECT SUM(amount) FROM invoices
WHERE paid_at >= date('now', '-12 months')
GROUP BY strftime('%Y-%m', paid_at)
ORDER BY strftime('%Y-%m', paid_at)", ct: ct)).ToArray();
// ... Dau, Storage similarly
}
}
@* On the page: *@
<Chart Type="bar" Data="@Model.Revenue" Width="220" Height="42" />
Use-case 2 — small multiples / per-region
US-East
+8.2%
US-West
+3.1%
EU-Central
+14.0%
AP-Southeast
-1.4%
Component source — 80 lines, zero dependencies
@namespace MyApp.Components.Chart
<div class="ndui-chart" style="height: @($"{Height}px");">
<svg viewBox="0 0 @Width @Height" preserveAspectRatio="none"
role="img" aria-label="@AriaLabel">
@switch (Type)
{
case "bar": RenderBar(__builder); break;
case "line": RenderLine(__builder); break;
case "area": RenderArea(__builder); break;
}
</svg>
</div>
@code {
[Parameter] public string Type { get; set; } = "bar";
[Parameter, EditorRequired]
public IReadOnlyList<double> Data { get; set; } = Array.Empty<double>();
[Parameter] public int Width { get; set; } = 200;
[Parameter] public int Height { get; set; } = 60;
[Parameter] public string AriaLabel { get; set; } = "Chart";
private double Max => Data.Count == 0 ? 1 : Math.Max(1, Data.Max());
private void RenderBar(RenderTreeBuilder __builder)
{
if (Data.Count == 0) return;
var gap = 2;
var barWidth = (double)(Width - gap * (Data.Count - 1)) / Data.Count;
for (int i = 0; i < Data.Count; i++)
{
var h = (Data[i] / Max) * Height;
__builder.OpenElement(0, "rect");
__builder.AddAttribute(1, "x", i * (barWidth + gap));
__builder.AddAttribute(2, "y", Height - h);
__builder.AddAttribute(3, "width", barWidth);
__builder.AddAttribute(4, "height", h);
__builder.AddAttribute(5, "class", "ndui-chart-bar");
__builder.CloseElement();
}
}
// RenderLine / RenderArea share a BuildPath() helper — see docs for full source.
}
Full source + variant walkthroughs at Docs → Chart deep-dive.