BitMap is a generic, provider-pluggable interactive map component. It supports Leaflet, MapLibre GL, Mapbox GL, OpenLayers, ArcGIS, Azure Maps, and CesiumJS.

Notes

To use this component, install the
Bit.BlazorUI.Extras
nuget package.
BitMap<TMapProvider> is generic - choose a provider class as the type argument and pass a configured instance via Provider. Providers: BitLeafletMapProvider, BitMapLibreMapProvider, BitMapboxMapProvider, BitOpenLayersMapProvider, BitArcGisMapProvider, BitAzureMapsMapProvider, BitCesiumMapProvider. Mapbox, Azure Maps, and ArcGIS (for non-OSM basemaps) require an API key on the provider instance.

Usage


Basic

<div style="height:360px">
    <BitMap TMapProvider="BitLeafletMapProvider" />
</div>
The simplest usage: a Leaflet map centered on London using the bundled Leaflet 1.9.4 library (no CDN, no token).

Markers

<div style="height:380px">
    <BitMap TMapProvider="BitLeafletMapProvider"
            @ref="markersMapRef"
            Provider="@markersProvider"
            OnReady="OnMarkersReady"
            OnMarkerClick="OnMarkerClick"
            OnMarkerDragEnd="OnMarkerDragEnd" />
</div>
<div style="display:flex;gap:0.5rem;flex-wrap:wrap;align-items:center">
    <BitButton OnClick="AddRandomMarker">Add random marker</BitButton>
    <BitButton OnClick="ClearMarkers" Variant="BitVariant.Outline">Clear all</BitButton>
    <BitButton OnClick="OpenLondonPopup" Variant="BitVariant.Outline">Open London popup</BitButton>
    <BitButton OnClick="FitToMarkers" Variant="BitVariant.Outline">Fit to markers</BitButton>
</div>
<pre>@markersLog</pre>
@code {
    private BitMap<BitLeafletMapProvider> markersMapRef = default!;
    private readonly BitLeafletMapProvider markersProvider = new() { Center = new(48.8566, 2.3522), Zoom = 5 };
    private string markersLog = "Seed markers are added on OnReady. Try the buttons.";
    private int _markerCounter;
    
    private async Task OnMarkersReady()
    {
        await markersMapRef.AddMarker(new BitMapMarker
        {
            Id = "paris", Position = new(48.8566, 2.3522),
            Title = "Paris", PopupHtml = (MarkupString)"<b>Paris</b><br/>Click to open popup.",
        });
        await markersMapRef.AddMarker(new BitMapMarker
        {
            Id = "london", Position = new(51.5074, -0.1278),
            Title = "London", PopupHtml = (MarkupString)"<b>London</b><br/>Draggable marker.",
            Draggable = true,
            TooltipHtml = (MarkupString)"Drag me!",
        });
        await markersMapRef.FitBoundsToMarkers();
    }
    
    private async Task AddRandomMarker()
    {
        _markerCounter++;
        var id = $"m{_markerCounter}";
    
        // Scatter inside the current viewport so new markers are always visible.
        var view = await markersMapRef.GetView();
        var sw = view.Bounds.SouthWest;
        var ne = view.Bounds.NorthEast;
    
        var latSpan = ne.Latitude - sw.Latitude;
        var lngSpan = ne.Longitude - sw.Longitude;
        if (lngSpan < 0) lngSpan += 360; // antimeridian
    
        const double inset = 0.1;
        var lat = sw.Latitude + (inset + Random.Shared.NextDouble() * (1 - 2 * inset)) * latSpan;
        var lng = sw.Longitude + (inset + Random.Shared.NextDouble() * (1 - 2 * inset)) * lngSpan;
        lat = Math.Clamp(lat, -85, 85);
        if (lng > 180) lng -= 360;
        else if (lng < -180) lng += 360;
    
        // Roll a coin so some markers come in draggable.
        var draggable = Random.Shared.Next(2) == 0;
    
        await markersMapRef.AddMarker(new BitMapMarker
        {
            Id = id, Position = new(lat, lng),
            Title = $"Marker {id}{(draggable ? " (draggable)" : "")}",
            PopupHtml = (MarkupString)($"Marker <code>{id}</code><br/>{lat:F4}, {lng:F4}" +
                        (draggable ? "<br/><i>Drag me!</i>" : "")),
            Draggable = draggable,
            TooltipHtml = draggable ? (MarkupString?)(MarkupString)"Drag me!" : null,
        });
        markersLog = $"Added {id}{(draggable ? " (draggable)" : "")} at {lat:F4}, {lng:F4}";
    }
    
    private async Task ClearMarkers()
    {
        await markersMapRef.ClearMarkers();
        markersLog = "All markers cleared.";
    }
    
    private async Task OpenLondonPopup()
    {
        await markersMapRef.OpenMarkerPopup("london");
        markersLog = "Opened London popup.";
    }
    
    private async Task FitToMarkers()
    {
        await markersMapRef.FitBoundsToMarkers();
        markersLog = "Fitted view to all markers.";
    }
    
    private Task OnMarkerClick(string id)
    {
        markersLog = $"Marker click: {id}";
        return Task.CompletedTask;
    }
    
    private Task OnMarkerDragEnd(BitMapMarkerDragEndArgs e)
    {
        markersLog = $"Drag end {e.Id} → {e.Position.Latitude:F5}, {e.Position.Longitude:F5}";
        return Task.CompletedTask;
    }
}
                    
Add markers with HTML popups, tooltips, and drag support via the imperative API. Always wait for OnReady before calling map methods.



Seed markers are added on OnReady. Try the buttons.

Vectors

<div style="height:380px">
    <BitMap TMapProvider="BitLeafletMapProvider"
            @ref="vectorsMapRef"
            Provider="@vectorsProvider"
            OnReady="OnVectorsReady"
            OnVectorClick="OnVectorClick" />
</div>
<div style="display:flex;gap:0.5rem;flex-wrap:wrap">
    <BitButton OnClick="RedrawVectors">Redraw</BitButton>
    <BitButton OnClick="ClearVectors" Variant="BitVariant.Outline">Clear vectors</BitButton>
</div>
<pre>@vectorsLog</pre>
@code {
    private BitMap<BitLeafletMapProvider> vectorsMapRef = default!;
    private readonly BitLeafletMapProvider vectorsProvider = new() { Center = new(37.7749, -122.4194), Zoom = 12 };
    private string vectorsLog = "Click Redraw to draw shapes, then click a shape.";
    
    private async Task OnVectorsReady() => await DrawVectors();
    
    private async Task DrawVectors()
    {
        await vectorsMapRef.AddPolyline("route",
        [
            new(37.80, -122.42), new(37.79, -122.41),
            new(37.78, -122.40), new(37.77, -122.395),
        ], new BitMapVectorPathStyle { Color = "#f85149", Weight = 5, Opacity = 0.9 });
    
        await vectorsMapRef.AddPolygon("park",
        [
            new(37.769, -122.486), new(37.771, -122.475),
            new(37.765, -122.472), new(37.762, -122.482),
        ], new BitMapVectorPathStyle { Color = "#3fb950", FillOpacity = 0.35, Weight = 2 });
    
        await vectorsMapRef.AddCircle("radius", new(37.7849, -122.4094), 900,
            new BitMapVectorPathStyle { Color = "#58a6ff", FillOpacity = 0.15, Weight = 2 });
    
        await vectorsMapRef.AddRectangle("box",
            new BitMapLatLngBounds(new(37.748, -122.44), new(37.756, -122.42)),
            new BitMapVectorPathStyle { Color = "#d29922", FillOpacity = 0.12, Weight = 2, DashArray = "6,4" });
    
        await vectorsMapRef.FitBounds(
            new BitMapLatLngBounds(new(37.755, -122.49), new(37.805, -122.38)));
    }
    
    private async Task RedrawVectors()
    {
        await vectorsMapRef.ClearVectorLayers();
        await DrawVectors();
        vectorsLog = "Vectors redrawn.";
    }
    
    private async Task ClearVectors()
    {
        await vectorsMapRef.ClearVectorLayers();
        vectorsLog = "All vector layers cleared.";
    }
    
    private Task OnVectorClick(BitMapVectorClickArgs e)
    {
        // e.Kind = "polyline" | "polygon" | "circle" | "rectangle"
        // e.LayerId = the id you passed to AddPolyline/AddPolygon/…
        vectorsLog = $"{e.Kind} \"{e.LayerId}\" @ {e.Position.Latitude:F5}, {e.Position.Longitude:F5}";
        return Task.CompletedTask;
    }
}
                    
Draw polylines, polygons, circles, and rectangles. Each layer has a string id so you can remove or replace it individually. Clicking a shape raises OnVectorClick.



Click Redraw to draw shapes, then click a shape.

GeoJSON

<div style="height:380px">
    <BitMap TMapProvider="BitLeafletMapProvider"
            @ref="geoJsonMapRef"
            Provider="@geoJsonProvider"
            OnGeoJsonFeatureClick="OnGeoJsonFeatureClick" />
</div>
<div style="display:flex;gap:0.5rem;flex-wrap:wrap">
    <BitButton OnClick="LoadGeoJson">Load GeoJSON</BitButton>
    <BitButton OnClick="RemoveGeoJson" Variant="BitVariant.Outline">Remove layer</BitButton>
</div>
<pre>@geoJsonLog</pre>
@code {
    private BitMap<BitLeafletMapProvider> geoJsonMapRef = default!;
    private readonly BitLeafletMapProvider geoJsonProvider = new() { Center = new(40.7128, -74.0060), Zoom = 11 };
    private string geoJsonLog = "Click 'Load GeoJSON', then click a feature.";
    
    private async Task LoadGeoJson()
    {
        await geoJsonMapRef.RemoveLayer("demo");
        await geoJsonMapRef.AddGeoJson("demo", SampleGeoJson,
            new BitMapVectorPathStyle { Color = "#a371f7", Weight = 3, FillOpacity = 0.25 });
        await geoJsonMapRef.FitBounds(new BitMapLatLngBounds(new(40.71, -74.03), new(40.83, -73.96)));
        geoJsonLog = "GeoJSON loaded. Click a feature.";
    }
    
    private async Task RemoveGeoJson()
    {
        await geoJsonMapRef.RemoveLayer("demo");
        geoJsonLog = "Layer \"demo\" removed.";
    }
    
    private Task OnGeoJsonFeatureClick(BitMapGeoJsonFeatureClickArgs e)
    {
        // e.LayerId = "demo"
        // e.Properties = JsonElement of feature.properties
        var name = "(no name)";
        if (e.Properties.ValueKind == System.Text.Json.JsonValueKind.Object
            && e.Properties.TryGetProperty("name", out var n))
        {
            name = n.ValueKind == System.Text.Json.JsonValueKind.String ? n.GetString() : n.ToString();
        }
        geoJsonLog = $"Layer {e.LayerId} - properties.name = {name}";
        return Task.CompletedTask;
    }
    
    // Minimal GeoJSON FeatureCollection used by LoadGeoJson above.
    private const string SampleGeoJson = """
        {
          "type": "FeatureCollection",
          "features": [
            {
              "type": "Feature",
              "properties": { "name": "Central Park" },
              "geometry": {
                "type": "Polygon",
                "coordinates": [[
                  [-73.981, 40.768], [-73.958, 40.768],
                  [-73.958, 40.800], [-73.981, 40.800],
                  [-73.981, 40.768]
                ]]
              }
            },
            {
              "type": "Feature",
              "properties": { "name": "Brooklyn Bridge" },
              "geometry": {
                "type": "LineString",
                "coordinates": [[-73.9969, 40.7061], [-73.9875, 40.7026]]
              }
            }
          ]
        }
        """;
}
                    
Load any GeoJSON string as a styled layer. Feature clicks forward the feature's properties as a JsonElement.



Click 'Load GeoJSON', then click a feature.

Custom tiles

<div style="display:flex;gap:0.5rem;flex-wrap:wrap;margin-bottom:0.75rem">
    <BitButton OnClick='() => SetTileProvider("osm")'
               Variant="@(tileProvider == "osm" ? BitVariant.Fill : BitVariant.Outline)">OSM default</BitButton>
    <BitButton OnClick='() => SetTileProvider("carto")'
               Variant="@(tileProvider == "carto" ? BitVariant.Fill : BitVariant.Outline)">Carto Voyager</BitButton>
    <BitButton OnClick='() => SetTileProvider("topo")'
               Variant="@(tileProvider == "topo" ? BitVariant.Fill : BitVariant.Outline)">OpenTopoMap</BitButton>
</div>

@* @key forces a new map instance when the provider changes *@
<div style="height:360px">
    <BitMap TMapProvider="BitLeafletMapProvider" @key="tileProvider" Provider="@currentTileLeafletProvider" />
</div>
@code {
    private string tileProvider = "osm";
    private BitLeafletMapProvider currentTileLeafletProvider = new() { Center = new(51.505, -0.09), Zoom = 13 };
    
    private void SetTileProvider(string p)
    {
        tileProvider = p;
        currentTileLeafletProvider = p switch
        {
            "carto" => new BitLeafletMapProvider
            {
                Center = new(20, 0), Zoom = 2,
                TileUrl = "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png",
                TileAttribution = "&copy; OpenStreetMap contributors &copy; <a href=\"https://carto.com/attributions\">CARTO</a>",
            },
            "topo" => new BitLeafletMapProvider
            {
                Center = new(46.5, 11.3), Zoom = 10,
                TileUrl = "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
                TileAttribution = "Map data: &copy; OpenStreetMap contributors, SRTM | Map style: &copy; OpenTopoMap",
                TileMaxZoom = 17,
            },
            _ => new BitLeafletMapProvider { Center = new(51.505, -0.09), Zoom = 13 },
        };
    }
}
                    
Switch the base tile layer at runtime by updating the Provider parameter. Any Leaflet-compatible XYZ template is accepted.

Events

<div style="height:320px">
    <BitMap TMapProvider="BitLeafletMapProvider"
            @ref="eventsMapRef"
            Provider="@eventsProvider"
            OnClick="OnMapClick"
            OnDoubleClick="OnMapDoubleClick"
            OnViewChanged="OnViewChanged" />
</div>
<div style="display:flex;gap:0.5rem;flex-wrap:wrap">
    <BitButton OnClick="FlyToTokyo">Fly to Tokyo</BitButton>
    <BitButton OnClick="ReadView" Variant="BitVariant.Outline">Log viewport</BitButton>
</div>
<pre>@eventsLog</pre>
@code {
    private BitMap<BitLeafletMapProvider> eventsMapRef = default!;
    private readonly BitLeafletMapProvider eventsProvider = new() { Center = new(35.6762, 139.6503), Zoom = 11 };
    private string eventsLog = "Pan/zoom or click the map.";
    
    private Task OnMapClick(BitMapLatLng p)
    {
        eventsLog = $"Click → {p.Latitude:F5}, {p.Longitude:F5}";
        return Task.CompletedTask;
    }
    
    private Task OnMapDoubleClick(BitMapLatLng p)
    {
        eventsLog = $"Double-click → {p.Latitude:F5}, {p.Longitude:F5}";
        return Task.CompletedTask;
    }
    
    private Task OnViewChanged(BitMapViewState v)
    {
        eventsLog = $"View: zoom {v.Zoom:F1}, center {v.Center.Latitude:F4},{v.Center.Longitude:F4}";
        return Task.CompletedTask;
    }
    
    private async Task FlyToTokyo()
    {
        await eventsMapRef.FlyTo(new(35.6762, 139.6503), 12);
        eventsLog = "Flying to Tokyo…";
    }
    
    private async Task ReadView()
    {
        var v = await eventsMapRef.GetView();
        eventsLog = $"GetView → zoom {v.Zoom:F2}, center {v.Center.Latitude:F4},{v.Center.Longitude:F4}, " +
                    $"NE {v.Bounds.NorthEast.Latitude:F4},{v.Bounds.NorthEast.Longitude:F4}";
    }
}
                    
OnClick returns the clicked coordinate. OnViewChanged fires after every pan or zoom. Use FlyTo for animated navigation and InvalidateSize after a container resize.



Pan/zoom or click the map.

Advanced

<div style="display:flex;gap:1rem;flex-wrap:wrap;margin-bottom:0.75rem">
    <BitToggle Value="advScrollWheel"
               ValueChanged="v => { advScrollWheel = v; BuildAdvancedProvider(); }"
               Text="Scroll wheel zoom" />
    <BitToggle Value="advDragging"
               ValueChanged="v => { advDragging = v; BuildAdvancedProvider(); }"
               Text="Dragging" />
    <BitToggle Value="advScaleBar"
               ValueChanged="v => { advScaleBar = v; BuildAdvancedProvider(); }"
               Text="Scale bar" />
    <BitToggle Value="advMaxBounds"
               ValueChanged="v => { advMaxBounds = v; BuildAdvancedProvider(); }"
               Text="Limit pan (London)" />
</div>

@* Bind a stable field, not a method call: a method call reallocates the provider on every render. *@
<div style="height:380px">
    <BitMap TMapProvider="BitLeafletMapProvider"
            @ref="advMapRef"
            Provider="@advProvider"
            OnReady="OnAdvancedReady"
            OnDoubleClick="OnAdvancedDoubleClick" />
</div>

<div style="display:flex;gap:0.5rem;flex-wrap:wrap">
    <BitButton OnClick="AddTooltipMarkers">Add tooltip markers + fit</BitButton>
    <BitButton OnClick="ToggleTileOverlay"
               Variant="BitVariant.Outline">@(advOverlayOn ? "Remove overlay" : "Add tile overlay")</BitButton>
    <BitButton OnClick="ReadAdvancedView" Variant="BitVariant.Outline">Log viewport</BitButton>
</div>
<pre>@advLog</pre>
@code {
    private BitMap<BitLeafletMapProvider> advMapRef = default!;
    private bool advScrollWheel = true;
    private bool advDragging = true;
    private bool advScaleBar = true;
    private bool advMaxBounds;
    private bool advOverlayOn;
    private string advLog = "Toggle options or use the buttons.";
    
    private BitLeafletMapProvider advProvider = new()
    {
        Center = new(51.5074, -0.1278), Zoom = 11,
        ScrollWheelZoom = true,
        Dragging = true,
        ShowScaleControl = true,
        MaxBounds = null,
    };
    
    // Mutate the stable field only when an option actually changes - not on every render.
    private BitLeafletMapProvider BuildAdvancedProvider()
    {
        advProvider = new BitLeafletMapProvider
        {
            Center = new(51.5074, -0.1278), Zoom = 11,
            ScrollWheelZoom = advScrollWheel,
            Dragging = advDragging,
            ShowScaleControl = advScaleBar,
            MaxBounds = advMaxBounds
                ? new BitMapLatLngBounds(new(51.25, -0.55), new(51.75, 0.35))
                : null,
        };
        // Rebuilding the provider replaces the underlying Leaflet map instance,
        // so any previously-added overlays no longer exist on the new map.
        // Reset the toggle state so the UI label/branch reflects that.
        advOverlayOn = false;
        return advProvider;
    }
    
    private async Task OnAdvancedReady() => await AddTooltipMarkers();
    
    private async Task AddTooltipMarkers()
    {
        await advMapRef.ClearMarkers();
        await advMapRef.AddMarker(new BitMapMarker { Id = "a", Position = new(51.52, -0.10), TooltipHtml = (MarkupString)"<b>West End</b>", PopupHtml = (MarkupString)"Popup A", ZIndexOffset = 10 });
        await advMapRef.AddMarker(new BitMapMarker { Id = "b", Position = new(51.50, -0.08), TooltipHtml = (MarkupString)"City", PopupHtml = (MarkupString)"Popup B" });
        await advMapRef.AddMarker(new BitMapMarker { Id = "c", Position = new(51.48, -0.06), TooltipHtml = (MarkupString)"South Bank", PopupHtml = (MarkupString)"Popup C" });
        await advMapRef.FitBoundsToMarkers(56);
        advLog = "Three tooltip markers added; view fitted.";
    }
    
    private async Task ToggleTileOverlay()
    {
        if (advOverlayOn)
        {
            await advMapRef.RemoveTileOverlay("labels");
            advOverlayOn = false;
            advLog = "Tile overlay removed.";
        }
        else
        {
            await advMapRef.AddTileOverlay(new BitMapTileOverlay
            {
                Id = "labels",
                UrlTemplate = "https://tiles.stadiamaps.com/tiles/stamen_toner_labels/{z}/{x}/{y}{r}.png",
                Attribution = "Map tiles by Stamen Design, hosted by Stadia Maps. Data by OpenStreetMap.",
                Opacity = 0.85,
                ZIndex = 400,
                MaxZoom = 20,
            });
            advOverlayOn = true;
            advLog = "Tile overlay added (may fail if the tile host blocks your origin).";
        }
    }
    
    private async Task ReadAdvancedView()
    {
        var v = await advMapRef.GetView();
        advLog = $"GetView → zoom {v.Zoom:F2}, center {v.Center.Latitude:F4},{v.Center.Longitude:F4}, " +
                 $"NE {v.Bounds.NorthEast.Latitude:F4},{v.Bounds.NorthEast.Longitude:F4}";
    }
    
    private Task OnAdvancedDoubleClick(BitMapLatLng p)
    {
        advLog = $"Double-click at {p.Latitude:F4}, {p.Longitude:F4}";
        return Task.CompletedTask;
    }
}
                    
Interaction toggles, scale bar, pan limits (MaxBounds), marker tooltips, z-order, and tile overlays.



Toggle options or use the buttons.

MapLibre GL

<div style="height:360px">
    <BitMap TMapProvider="BitMapLibreMapProvider" Provider="@maplibreProvider" />
</div>
@code {
    // Bind a stable field so the provider isn't reallocated on every render.
    private readonly BitMapLibreMapProvider maplibreProvider = new() { Center = new(48.8566, 2.3522), Zoom = 5 };
}
                    
MapLibre GL JS is loaded from unpkg on first render. No token required for the default demo style.

OpenLayers

<div style="height:360px">
    <BitMap TMapProvider="BitOpenLayersMapProvider" Provider="@olProvider" />
</div>
@code {
    // Bind a stable field so the provider isn't reallocated on every render.
    private readonly BitOpenLayersMapProvider olProvider = new() { Center = new(35.6762, 139.6503), Zoom = 4 };
}
                    
OpenLayers 10 is loaded as ES modules from esm.sh. No token required; defaults to OpenStreetMap tiles.

Mapbox GL (token required)

<div style="height:360px">
    <BitMap TMapProvider="BitMapboxMapProvider" Provider="@mapboxProvider" />
</div>
@code {
    // Get your token from https://account.mapbox.com/access-tokens/
    // and pass it via the AccessToken property on BitMapboxMapProvider.
    // Bind a stable field so the provider isn't reallocated on every render.
    private readonly BitMapboxMapProvider mapboxProvider = new()
    {
        AccessToken = "YOUR_MAPBOX_TOKEN",
        Center = new(40, 0),
        Zoom = 2,
    };
}
                    
Mapbox GL JS requires a public access token to load mapbox:// styles.

To get started:
  1. Create a free account at mapbox.com
  2. Copy your default public token from the Access Tokens page
  3. Pass it to BitMapboxMapProvider.AccessToken
Without a valid token the map canvas will remain blank.

Security: only ship a public token (pk.*) to the browser, never a secret token (sk.*). Public tokens are visible to anyone using your site, so restrict each token to your production domains via the URL allowlist on the Access Tokens page, set a billing cap, and use a separate token per environment so you can rotate or revoke without downtime. For stricter scenarios, mint a short-lived token from a server endpoint or proxy tile requests through your backend.

ArcGIS Maps SDK

<div style="height:360px">
    <BitMap TMapProvider="BitArcGisMapProvider" Provider="@arcGisProvider" />
</div>
@code {
    // Bind a stable field so the provider isn't reallocated on every render.
    private readonly BitArcGisMapProvider arcGisProvider = new() { Center = new(40, 0), Zoom = 2, BasemapId = "osm" };
}
                    
ArcGIS Maps SDK 5.0 is loaded as an ES module from the Esri CDN. The osm basemap works without an API key.

Security: if you supply an API key for non-OSM basemaps, configure HTTP referrer restrictions on the key in the ArcGIS Developer dashboard so it can only be used from your own domains, and scope it to the minimum required services.

Azure Maps (subscription key required)

<div style="height:360px">
    <BitMap TMapProvider="BitAzureMapsMapProvider" Provider="@azureMapsProvider" />
</div>
@code {
    // Get your key from Azure Portal > Maps account > Authentication > Shared Key
    // and pass it via the SubscriptionKey property on BitAzureMapsMapProvider.
    // Bind a stable field so the provider isn't reallocated on every render.
    private readonly BitAzureMapsMapProvider azureMapsProvider = new()
    {
        SubscriptionKey = "YOUR_AZURE_MAPS_KEY",
        Center = new(40, 0),
        Zoom = 2,
    };
}
                    
Azure Maps Web SDK v3 requires a subscription key for authentication.

To get started:
  1. Create an Azure Maps account in the Azure Portal
  2. Navigate to your Maps account → Authentication → Shared Key Authentication
  3. Copy the Primary Key and pass it to BitAzureMapsMapProvider.SubscriptionKey
Without a valid key the map will not render.

Security: shipping a shared subscription key to the browser is fine for demos but not recommended for production. For production, prefer Microsoft Entra ID or SAS token authentication: a backend mints a short-lived token and the client uses that instead of the primary key. If you must use a subscription key, restrict allowed origins on the Maps account and set usage alerts so a leaked key cannot quietly drain your quota.

CesiumJS 3D globe

<div style="height:420px">
    <BitMap TMapProvider="BitCesiumMapProvider" Provider="@cesiumProvider" />
</div>
@code {
    // Bind a stable field so the provider isn't reallocated on every render.
    private readonly BitCesiumMapProvider cesiumProvider = new() { Center = new(20, 0), Zoom = 2, SceneMode = "scene3d" };
}
                    
CesiumJS renders a 3D globe. OSM imagery and smooth-ellipsoid terrain work without a token. A Cesium ion token unlocks Cesium World Terrain and Bing imagery.

Security: if you provide a Cesium ion token, create a dedicated token in the Cesium ion dashboard, grant only the asset access it needs, and add your production domains to the token's allowed URLs list so a copied token cannot be used from other origins.

API

BitMap parameters

Name Type Default value Description
TMapProvider Type (generic) The map provider type. One of: BitLeafletMapProvider, BitMapLibreMapProvider, BitMapboxMapProvider, BitOpenLayersMapProvider, BitArcGisMapProvider, BitAzureMapsMapProvider, BitCesiumMapProvider.
Provider TMapProvider? null Provider configuration instance (center, zoom, tokens, etc.). When null a default instance is created.
ChildContent RenderFragment? null Optional content rendered above the map canvas.
ReplayStateOnProviderSwap bool false When true, imperatively-added markers, vector layers, and tile overlays are replayed after a destructive provider swap (different JsObjectName).
OnReady EventCallback Fires after the map is ready for imperative calls. Fires once on initial mount, and fires again after a destructive provider swap each time the new provider becomes ready.
OnClick EventCallback<BitMapLatLng> Fires when the user clicks the map canvas.
OnDoubleClick EventCallback<BitMapLatLng> Fires when the user double-clicks the map.
OnViewChanged EventCallback<BitMapViewState> Fires whenever the map view changes.
OnMarkerClick EventCallback<string> Fires when the user clicks a marker (argument is the marker id).
OnMarkerDragEnd EventCallback<BitMapMarkerDragEndArgs> Fires when a draggable marker is dropped.
OnVectorClick EventCallback<BitMapVectorClickArgs> Fires when the user clicks a vector layer.
OnGeoJsonFeatureClick EventCallback<BitMapGeoJsonFeatureClickArgs> Fires when the user clicks a GeoJSON feature.
OnInteropError EventCallback<BitMapInteropErrorArgs> Fires when an interop call into the underlying provider fails. Lets consumers surface errors that the component would otherwise swallow to prevent circuit-breaking exceptions.

BitMap public members

Name Type Default value Description
IsReady bool false True after the map is ready for interop calls.
GetView Func<ValueTask<BitMapViewState>> Returns a snapshot of the current viewport.
SetView Func<BitMapLatLng, double?, bool, ValueTask> Pan and optionally zoom to the given center.
FlyTo Func<BitMapLatLng, double?, ValueTask> Animated pan/zoom to the given center.
FitBounds Func<BitMapLatLngBounds, int, ValueTask> Fit the view to the given bounding box.
FitBoundsToMarkers Func<int, ValueTask> Fit the view to include all current markers.
InvalidateSize Func<ValueTask> Recalculate map size after a container resize.
AddMarker Func<BitMapMarker, ValueTask> Add a marker to the map.
RemoveMarker Func<string, ValueTask> Remove a marker by id.
ClearMarkers Func<ValueTask> Remove all markers.
SetMarkerPosition Func<string, BitMapLatLng, ValueTask> Move a marker to a new position.
OpenMarkerPopup Func<string, ValueTask> Open a marker's popup.
SyncMarkers Func<IEnumerable<BitMapMarker>, ValueTask> Replace all markers in one batch.
AddPolyline Func<string, IReadOnlyList<BitMapLatLng>, BitMapVectorPathStyle?, ValueTask> Add a polyline.
AddPolygon Func<string, IReadOnlyList<BitMapLatLng>, BitMapVectorPathStyle?, ValueTask> Add a polygon.
AddCircle Func<string, BitMapLatLng, double, BitMapVectorPathStyle?, ValueTask> Add a circle (radius in meters).
AddRectangle Func<string, BitMapLatLngBounds, BitMapVectorPathStyle?, ValueTask> Add a rectangle.
AddGeoJson Func<string, string, BitMapVectorPathStyle?, ValueTask> Add a GeoJSON layer.
RemoveLayer Func<string, ValueTask> Remove a vector layer by id.
ClearVectorLayers Func<ValueTask> Remove all vector layers.
AddTileOverlay Func<BitMapTileOverlay, ValueTask> Add a tile overlay above the base map.
RemoveTileOverlay Func<string, ValueTask> Remove a tile overlay by id.

BitComponentBase parameters

Name Type Default value Description
AriaLabel string? null Gets or sets the accessible label for the component, used by assistive technologies.
Class string? null Gets or sets the CSS class name(s) to apply to the rendered element.
Dir BitDir? null Gets or sets the text directionality for the component's content.
ForceAnimation bool false Gets or sets a value indicating whether the component's animations play at their full duration even when reduced motion is requested.
HtmlAttributes Dictionary<string, object> new Dictionary<string, object>() Captures additional HTML attributes to be applied to the rendered element, in addition to the component's parameters.
Id string? null Gets or sets the unique identifier for the component's root element.
IsEnabled bool true Gets or sets a value indicating whether the component is enabled and can respond to user interaction.
Style string? null Gets or sets the CSS style string to apply to the rendered element.
TabIndex string? null Gets or sets the tab order index for the component when navigating with the keyboard.
Visibility BitVisibility BitVisibility.Visible Gets or sets the visibility state (visible, hidden, or collapsed) of the component.

BitComponentBase public members

Name Type Default value Description
UniqueId Guid Guid.NewGuid() Gets the readonly unique identifier for the component's root element, assigned when the component instance is constructed.
RootElement ElementReference Gets the reference to the root HTML element associated with this component.

BitVisibility enum

Name Value Description
Visible 0 The content of the component is visible.
Hidden 1 The content of the component is hidden, but the space it takes on the page remains (visibility:hidden).
Collapsed 2 The component is hidden (display:none).

BitDir enum

Name Value Description
Ltr 0 Ltr (left to right) is to be used for languages that are written from the left to the right (like English).
Rtl 1 Rtl (right to left) is to be used for languages that are written from the right to the left (like Arabic).
Auto 2 Auto lets the user agent decide. It uses a basic algorithm as it parses the characters inside the element until it finds a character with a strong directionality, then applies that directionality to the whole element.

Feedback

You can give us your feedback through our GitHub repo by filing a new Issue or starting a new Discussion.


Or you can review / edit this page on GitHub.


Or you can review / edit this component on GitHub.