MatPlotLibNet.Blazor
1.27.0
See the version list below for details.
dotnet add package MatPlotLibNet.Blazor --version 1.27.0
NuGet\Install-Package MatPlotLibNet.Blazor -Version 1.27.0
<PackageReference Include="MatPlotLibNet.Blazor" Version="1.27.0" />
<PackageVersion Include="MatPlotLibNet.Blazor" Version="1.27.0" />
<PackageReference Include="MatPlotLibNet.Blazor" />
paket add MatPlotLibNet.Blazor --version 1.27.0
#r "nuget: MatPlotLibNet.Blazor, 1.27.0"
#:package MatPlotLibNet.Blazor@1.27.0
#addin nuget:?package=MatPlotLibNet.Blazor&version=1.27.0
#tool nuget:?package=MatPlotLibNet.Blazor&version=1.27.0
MatPlotLibNet.Blazor
Blazor components for the MatPlotLibNet charting library. They render charts as inline SVG, with optional real-time server push. Since v1.2.0 the interaction also runs in both directions: zoom, pan, reset and legend-toggle events travel from the browser back to the .NET server. The server updates the authoritative Figure and streams the new SVG back.
Installation
dotnet add package MatPlotLibNet.Blazor
Components
MplChart — static chart
@using MatPlotLibNet
@using MatPlotLibNet.Blazor
<MplChart Figure="@_figure" CssClass="my-chart" />
@code {
private Figure _figure = Plt.Create()
.WithTitle("Sales")
.Bar(["Q1", "Q2", "Q3"], [100, 200, 150])
.Build();
}
MplLiveChart — one-way server push via SignalR
<MplLiveChart ChartId="dashboard-1"
HubUrl="/charts-hub"
CssClass="live-chart" />
The component connects to a ChartHub endpoint. It updates automatically when the server calls IChartPublisher.PublishSvgAsync for this chart id. When the figure has .WithServerInteraction(...) the component also sends the viewer's gestures: it hands the script inside the SVG the connection its client holds, so the page needs no JavaScript and sets no window.__mpl_signalr_connection, and a redraw that arrives in the middle of a drag is mounted when the chart is idle.
Bidirectional interaction (v1.2.0)
Add .WithServerInteraction(...) on the figure builder to send browser interaction to the server. The dispatcher script embedded in the SVG invokes the hub's OnZoom, OnPan, OnReset and OnLegendToggle methods. The server then updates the registered figure and publishes the new SVG through the same fan-out path that MplLiveChart already listens on.
For client-side-only interaction (no server round-trip), use .WithBrowserInteraction() instead. It switches on pan and zoom, legend toggle (click) and legend drag (press-and-hold to reposition; v1.7.2), treemap drilldown, sankey hover, 3D rotation, rich tooltips, highlight and brush selection, all automatically. The library detects which scripts are relevant per chart and emits only those.
@page "/interactive"
@using MatPlotLibNet.AspNetCore
@using MatPlotLibNet.Rendering.Svg
@using MatPlotLibNet.Transforms
@inject FigureRegistry Registry
@implements IDisposable
<div id="host">@((MarkupString)_svg)</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.0/signalr.min.js"></script>
<script>
(function () {
// Mounts a chart's SVG markup in host and runs the scripts inside it. A <script> that arrives through
// innerHTML never runs, so each one is replaced by a fresh copy, which the browser runs as it is inserted.
// A chart the viewer is in the middle of a drag or a band on says so (data-mpl-busy): replacing it would cut
// the gesture off, so the latest picture waits for the chart's mpl:idle event.
function mplMountSvg(host, svgMarkup) {
var busy = host.querySelector('svg[data-mpl-busy]');
if (busy) {
var first = !host.__mplHeldBack;
host.__mplHeldBack = svgMarkup;
if (first) busy.addEventListener('mpl:idle', function () {
var held = host.__mplHeldBack; host.__mplHeldBack = null;
if (held) mplMountSvg(host, held);
}, { once: true });
return;
}
host.innerHTML = svgMarkup;
host.querySelectorAll('script').forEach(function (inert) {
var live = document.createElementNS(inert.namespaceURI, 'script');
for (var i = 0; i < inert.attributes.length; i++)
live.setAttribute(inert.attributes[i].name, inert.attributes[i].value);
live.textContent = inert.textContent;
inert.parentNode.replaceChild(live, inert);
});
}
var conn = new signalR.HubConnectionBuilder()
.withUrl('/charts-hub').withAutomaticReconnect().build();
window.__mpl_signalr_connection = conn;
conn.on('UpdateChartSvg', function (id, svg) {
if (id === 'blazor-interactive') mplMountSvg(document.getElementById('host'), svg);
});
conn.start().then(function () { return conn.invoke('Subscribe', 'blazor-interactive'); });
})();
</script>
@code {
private string _svg = string.Empty;
protected override void OnInitialized()
{
var figure = Plt.Create()
.WithTitle("Damped sine — server-authoritative")
.Plot(xs, ys)
.WithServerInteraction("blazor-interactive", i => i.All())
.Build();
figure.SubPlots[0].XAxis.Min = xs[0];
figure.SubPlots[0].XAxis.Max = xs[^1];
Registry.Register("blazor-interactive", figure);
_svg = new SvgTransform().Render(figure);
}
public void Dispose() => _ = Registry.UnregisterAsync("blazor-interactive");
}
On a page of your own, as above, the dispatcher script looks up window.__mpl_signalr_connection, so the <script> block only needs to run once per page. There is no per-chart wiring. With MplLiveChart none of it is needed. FigureRegistry.Register installs a background reader task for each chart, so the hub method returns in microseconds and the rendering happens asynchronously, off the request thread.
A runnable version ships in Samples/MatPlotLibNet.Samples.Blazor/Components/Pages/Interactive.razor, on route /interactive; it is one MplLiveChart.
Extension method
@((MarkupString)figure.ToMarkupString())
ToMarkupString() converts a Figure to an SVG MarkupString for direct rendering.
Dependencies
MatPlotLibNet(core)MatPlotLibNet.AspNetCore(for the bidirectional path; it needsFigureRegistryandChartHub)Microsoft.AspNetCore.SignalR.Client
License
MIT — Copyright (c) 2026 H.P. Gansevoort
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- MatPlotLibNet (>= 1.27.0)
- Microsoft.AspNetCore.SignalR.Client (>= 10.0.14)
-
net8.0
- MatPlotLibNet (>= 1.27.0)
- Microsoft.AspNetCore.SignalR.Client (>= 10.0.14)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.28.0 | 0 | 10/31/2026 |
| 1.27.0 | 41 | 10/30/2026 |
| 1.26.0 | 65 | 10/30/2026 |
| 1.25.0 | 75 | 10/29/2026 |
| 1.24.0 | 76 | 10/29/2026 |
| 1.23.0 | 92 | 10/27/2026 |
| 1.22.0 | 112 | 10/24/2026 |
| 1.21.0 | 107 | 10/23/2026 |
| 1.20.0 | 92 | 10/22/2026 |
| 1.19.0 | 104 | 10/21/2026 |
| 1.18.2 | 101 | 10/18/2026 |
| 1.18.1 | 94 | 10/12/2026 |
| 1.18.0 | 120 | 9/23/2026 |
| 1.17.1 | 112 | 9/13/2026 |
| 1.17.0 | 120 | 9/12/2026 |
| 1.16.0 | 113 | 9/12/2026 |
| 1.15.1 | 111 | 9/12/2026 |
| 1.15.0 | 115 | 9/11/2026 |
| 1.14.4 | 108 | 9/11/2026 |
| 1.14.3 | 117 | 8/30/2026 |
MatPlotLibNet 1.27.0. What changed in this release, and in every release before it, is written out in the changelog: https://github.com/xkqg/MatPlotLibNet/blob/main/CHANGELOG.md#1270