Creating a plugin
DP.Blazor.MapLibre is a wrapper library around the core MapLibre GL JS JavaScript API. You may want to interop with the MapLibre map in ways that cannot be achieved using the wrapper library, and for this, you can add a plugin.
At a high-level, a plugin can, but not exclusively:
- Maintain a reference to the MapLibre map object created by the core library.
- Import other JavaScript modules, kept separate from the core library.
- Use the MapLibre map object reference in its own JavaScript module(s).
- Expose a type-safe API similar to the core library.
Quick escape hatch - no plugin project needed
If all you need is to call one MapLibre GL JS method that the wrapper library hasn't caught up
to yet, a full plugin project is more ceremony than the job needs. MapLibre.NativeMap exposes
the underlying maplibregl.Map JS object directly, and IJSObjectReference.InvokeAsync/
InvokeVoidAsync call methods on it with no .js file required:
// Equivalent to calling a native map method, without writing any JavaScript.
await _map.NativeMap.InvokeVoidAsync("rotateTo", bearing, new { duration = 0 });
Reach for a full plugin (below) instead when you need real custom JS logic - helper functions, an external library, or several coordinated calls with your own state - not just one more native map method.
Steps
Create a Razor Class Library project.
dotnet new razorclasslib --name MyMapLibreRotationPluginThe existing files in the project are not relevant here and can be ignored/delete later.
Add a reference to the
DP.Blazor.MapLibreNuGet package.From the directory containing the project (
.csprojfile).dotnet add package DP.Blazor.MapLibreCreate a plugin JavaScript module.
A core rationale for creating a plugin is to interop with the MapLibre object that was initialised in the core libraries JavaScript module. This map object can be passed into our JavaScript module if we design it to receive it.
Create a file named
MyMapLibreRotationPlugin.jsin the/wwwrootfolder as below.let mapObject = {}; let pluginDotnetReference = {}; export function initialize(map, dotnetReference) { mapObject = map; pluginDotnetReference = dotnetReference; console.log("Plugin initialized"); } // See https://maplibre.org/maplibre-gl-js/docs/examples/animate-camera-around-point/ export function rotate(duration) { const rotate = (startTime) => { const elapsed = Date.now() - startTime; const progress = elapsed / duration; const degrees = progress * 360; mapObject.rotateTo(degrees % 360, { duration: 0 }); if (elapsed < duration) { requestAnimationFrame(() => rotate(startTime)); } }; rotate(Date.now()); }Accepting the
dotnetReferenceinto the JavaScript module enables call back to the plugin C# class. It's not used in this example, but the implementation does not differ from the official guidance. See Call .NET methods from JavaScript functions in ASP.NET Core Blazor. It could be used to notify the plugin C# class that the JavaScript module has initialized, or the rotation has completed for example.Create a plugin class.
Plugins must implement the
IMapLibrePlugininterface, and should load in any JavaScript modules in theInitializemethod, and perform any other setup needed.public class MyMapLibreRotationPlugin : IMapLibrePlugin { private DotNetObjectReference<MyMapLibreRotationPlugin> PluginDotNetReference { get; set; } = null!; private IJSObjectReference PluginJsModule { get; set; } = null!; public async Task Initialize(IJSObjectReference map, IJSRuntime runtime) { PluginDotNetReference = DotNetObjectReference.Create(this); PluginJsModule = await runtime .InvokeAsync<IJSObjectReference>("import", "./_content/MyMapLibreRotationPlugin/MyMapLibreRotationPlugin.js"); await PluginJsModule.InvokeVoidAsync("initialize", map, PluginDotNetReference); } public async ValueTask Rotate(int duration) => await PluginJsModule.InvokeVoidAsync("rotate", duration); public async ValueTask DisposeAsync() { try { await PluginJsModule.DisposeAsync(); } catch (JSDisconnectedException) { } catch (ObjectDisposedException) { } PluginDotNetReference?.Dispose(); PluginDotNetReference = null; } }- Since the JavaScript module is being loaded from a Razor Class Library, use a base-relative path
./_content/{NAMESPACE}/{MODULE_FILE_NAME}.js(works with<base href>on GitHub Pages and similar hosts). - The plugin is not responsible for disposing the
MapObjectsince a that reference was provided to it by the coreMapLibrecomponent during initialization.
- Since the JavaScript module is being loaded from a Razor Class Library, use a base-relative path
Add a reference to your plugin Razor Class Library project.
dotnet reference add MyMapLibreRotationPlugin.csproj --project MyBlazorProject.csprojRegister your plugin with the MapLibre component.
In the
OnAfterRenderAsyncmethod of the page/component that contains theMapLibrecomponent.private MapLibre? _map { get; set; } private MyMapLibreRotationPlugin _myMapLibreRotationPlugin = new(); protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { await _map.RegisterPlugin(_myMapLibreRotationPlugin); } }Use the capabilities exposed by your plugin.
For example, in the
OnMapLoadcall back for theMapLibrecomponent, you can use your plugin to invoke the capabilities it exposes.private async Task OnMapLoad(EventArgs args) { await _myMapLibreRotationPlugin.Rotate(2000); }
Reference implementations
The repository includes plugin projects you can use as starting points:
- Terra Draw plugin — drawing and geometry editing with Terra Draw
- Map Compare plugin — swipe and sync between two maps with maplibre-gl-compare
- Minimap plugin — overview minimap control (mapboxgl-minimap)
- Frame rate plugin — rendering performance overlay (mapbox-gl-framerate)
- Geo grid plugin — geographic graticule with labels (geogrid-maplibre-gl)
- Mapbox GL Draw plugin — reference plugin in the examples solution