N
data-display chart

Chart

Zero-dependency SVG bar / line / area.

$ ndui add chart
Component docs

Three chart types, one component, no dependencies

bar
Bar — monthly revenue
12 pts
line
Line — daily active users
30 pts
area
Area — storage used (30d)
30 pts
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.