Legend, colorbar, basemap switcher, terrain toggle, search, vector data loader, feature inspector, and HTML control components for MapLibre GL JS maps.
- Colorbar - Continuous gradient legends with built-in matplotlib colormaps
- Legend - Categorical legends with color swatches and labels
- BasemapControl - Interactive basemap switcher with 100+ providers from xyzservices
- TerrainControl - Toggle 3D terrain on/off using free AWS Terrarium elevation tiles
- SearchControl - Collapsible place search with geocoding and fly-to functionality
- VectorDatasetControl - Load GeoJSON files via file upload or drag-and-drop
- InspectControl - Click on features to view their properties/attributes
- HtmlControl - Flexible HTML content control for custom info panels
- Zoom-based Visibility - Show/hide components at specific zoom levels with
minzoom/maxzoom - React Support - First-class React components and hooks
- TypeScript - Full type definitions included
- 20+ Built-in Colormaps - viridis, plasma, terrain, jet, and more
npm install maplibre-gl-componentsimport maplibregl from 'maplibre-gl';
import { Colorbar, Legend, HtmlControl, BasemapControl, TerrainControl, SearchControl, VectorDatasetControl } from 'maplibre-gl-components';
import 'maplibre-gl-components/style.css';
const map = new maplibregl.Map({
container: 'map',
style: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
center: [-98, 38.5],
zoom: 4,
});
// Add a terrain toggle control
const terrainControl = new TerrainControl({
exaggeration: 1.5,
hillshade: true,
});
map.addControl(terrainControl, 'top-right');
// Add a search control
const searchControl = new SearchControl({
placeholder: 'Search for a place...',
flyToZoom: 14,
showMarker: true,
});
map.addControl(searchControl, 'top-right');
// Add a vector dataset loader (file upload and drag-drop)
const vectorControl = new VectorDatasetControl({
fitBounds: true,
});
map.addControl(vectorControl, 'top-left');
vectorControl.on('load', (event) => {
console.log('Loaded:', event.dataset?.filename);
});
// Add a basemap switcher
const basemapControl = new BasemapControl({
defaultBasemap: 'OpenStreetMap.Mapnik',
showSearch: true,
filterGroups: ['OpenStreetMap', 'CartoDB', 'Stadia', 'Esri'],
});
map.addControl(basemapControl, 'top-left');
// Add a colorbar
const colorbar = new Colorbar({
colormap: 'viridis',
vmin: 0,
vmax: 100,
label: 'Temperature',
units: '°C',
orientation: 'vertical',
});
map.addControl(colorbar, 'bottom-right');
// Add a legend
const legend = new Legend({
title: 'Land Cover',
items: [
{ label: 'Forest', color: '#228B22' },
{ label: 'Water', color: '#4169E1' },
{ label: 'Urban', color: '#808080' },
],
collapsible: true,
});
map.addControl(legend, 'bottom-left');
// Add an HTML control
const htmlControl = new HtmlControl({
html: '<div><strong>Stats:</strong> 1,234 features</div>',
});
map.addControl(htmlControl, 'top-left');
// Update HTML dynamically
htmlControl.setHtml('<div><strong>Stats:</strong> 5,678 features</div>');import { useState, useEffect, useRef } from 'react';
import maplibregl from 'maplibre-gl';
import { ColorbarReact, LegendReact, HtmlControlReact, BasemapReact, TerrainReact, SearchControlReact, VectorDatasetReact } from 'maplibre-gl-components/react';
import 'maplibre-gl-components/style.css';
function MyMap() {
const mapContainer = useRef<HTMLDivElement>(null);
const [map, setMap] = useState<maplibregl.Map | null>(null);
const [stats, setStats] = useState('Loading...');
useEffect(() => {
if (!mapContainer.current) return;
const mapInstance = new maplibregl.Map({
container: mapContainer.current,
style: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
center: [-98, 38.5],
zoom: 4,
});
mapInstance.on('load', () => setMap(mapInstance));
return () => mapInstance.remove();
}, []);
return (
<div style={{ width: '100%', height: '100vh' }}>
<div ref={mapContainer} style={{ width: '100%', height: '100%' }} />
{map && (
<>
<VectorDatasetReact
map={map}
fitBounds
position="top-left"
onDatasetLoad={(dataset) => console.log('Loaded:', dataset.filename)}
/>
<SearchControlReact
map={map}
placeholder="Search for a place..."
flyToZoom={14}
showMarker
position="top-right"
onResultSelect={(result) => console.log('Selected:', result.name)}
/>
<TerrainReact
map={map}
exaggeration={1.5}
hillshade
position="top-right"
onTerrainChange={(enabled) => console.log('Terrain:', enabled)}
/>
<BasemapReact
map={map}
defaultBasemap="OpenStreetMap.Mapnik"
showSearch
filterGroups={['OpenStreetMap', 'CartoDB', 'Stadia']}
position="top-left"
/>
<ColorbarReact
map={map}
colormap="viridis"
vmin={0}
vmax={100}
label="Temperature"
units="°C"
position="bottom-right"
/>
<LegendReact
map={map}
title="Categories"
items={[
{ label: 'Low', color: '#2166ac' },
{ label: 'High', color: '#b2182b' },
]}
position="bottom-left"
collapsible
/>
<HtmlControlReact
map={map}
html={`<div><strong>Stats:</strong> ${stats}</div>`}
position="top-left"
/>
</>
)}
</div>
);
}A continuous gradient colorbar control.
interface ColorbarOptions {
colormap?: ColormapName | string[]; // Colormap name or custom colors
colorStops?: ColorStop[]; // Fine-grained color control
vmin?: number; // Minimum value (default: 0)
vmax?: number; // Maximum value (default: 1)
label?: string; // Title/label
units?: string; // Units suffix
orientation?: 'horizontal' | 'vertical';
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
barThickness?: number; // Bar width/height in pixels
barLength?: number; // Bar length in pixels
ticks?: { count?: number; values?: number[]; format?: (v: number) => string };
visible?: boolean;
backgroundColor?: string;
opacity?: number;
fontSize?: number;
fontColor?: string;
minzoom?: number; // Min zoom level to show (default: 0)
maxzoom?: number; // Max zoom level to show (default: 24)
}
// Methods
colorbar.show()
colorbar.hide()
colorbar.update(options)
colorbar.getState()
colorbar.on(event, handler)
colorbar.off(event, handler)A categorical legend control.
interface LegendOptions {
title?: string;
items?: LegendItem[]; // { label, color, shape?, icon? }
position?: ControlPosition;
visible?: boolean;
collapsible?: boolean;
collapsed?: boolean;
width?: number;
maxHeight?: number;
swatchSize?: number;
backgroundColor?: string;
opacity?: number;
fontSize?: number;
fontColor?: string;
minzoom?: number; // Min zoom level to show (default: 0)
maxzoom?: number; // Max zoom level to show (default: 24)
}
interface LegendItem {
label: string;
color: string;
shape?: 'square' | 'circle' | 'line';
strokeColor?: string;
icon?: string; // URL to icon image
}
// Methods
legend.show()
legend.hide()
legend.expand()
legend.collapse()
legend.toggle()
legend.setItems(items)
legend.addItem(item)
legend.removeItem(label)
legend.update(options)
legend.getState()A flexible HTML content control.
interface HtmlControlOptions {
html?: string; // HTML content
element?: HTMLElement; // Or provide a DOM element
position?: ControlPosition;
visible?: boolean;
backgroundColor?: string;
padding?: number;
borderRadius?: number;
opacity?: number;
maxWidth?: number;
maxHeight?: number;
minzoom?: number; // Min zoom level to show (default: 0)
maxzoom?: number; // Max zoom level to show (default: 24)
}
// Methods
htmlControl.show()
htmlControl.hide()
htmlControl.setHtml(html) // Update HTML content
htmlControl.setElement(element) // Set DOM element
htmlControl.getElement() // Get content container
htmlControl.update(options)
htmlControl.getState()An interactive basemap switcher that loads providers from xyzservices.
interface BasemapControlOptions {
basemaps?: BasemapItem[]; // Custom basemaps array
providersUrl?: string; // URL to fetch providers.json (defaults to xyzservices)
defaultBasemap?: string; // Initial basemap ID (e.g., 'OpenStreetMap.Mapnik')
position?: ControlPosition;
visible?: boolean;
collapsible?: boolean; // Whether control is collapsible (default: true)
collapsed?: boolean; // Whether control starts collapsed (default: true)
displayMode?: 'dropdown' | 'gallery' | 'list'; // UI mode (default: 'dropdown')
showSearch?: boolean; // Show search input (default: true)
filterGroups?: string[]; // Only include these provider groups
excludeGroups?: string[]; // Exclude these provider groups
excludeBroken?: boolean; // Exclude broken providers (default: true)
backgroundColor?: string;
maxWidth?: number;
maxHeight?: number;
fontSize?: number;
fontColor?: string;
minzoom?: number;
maxzoom?: number;
}
interface BasemapItem {
id: string; // Unique identifier
name: string; // Display name
group?: string; // Provider group (e.g., 'OpenStreetMap')
url?: string; // XYZ tile URL template
style?: string; // MapLibre style URL
attribution?: string;
thumbnail?: string; // Preview image URL
maxZoom?: number;
minZoom?: number;
requiresApiKey?: boolean;
apiKey?: string;
}
// Methods
basemapControl.show()
basemapControl.hide()
basemapControl.expand()
basemapControl.collapse()
basemapControl.toggle()
basemapControl.setBasemap(basemapId) // Switch to a basemap
basemapControl.getBasemaps() // Get available basemaps
basemapControl.addBasemap(basemap) // Add a custom basemap
basemapControl.removeBasemap(id) // Remove a basemap
basemapControl.setApiKey(id, key) // Set API key for a basemap
basemapControl.getSelectedBasemap() // Get currently selected basemap
basemapControl.update(options)
basemapControl.getState()
basemapControl.on('basemapchange', handler) // Listen for basemap changesAvailable Provider Groups:
- OpenStreetMap, CartoDB, Stadia, Esri, OpenTopoMap
- Thunderforest, MapBox, MapTiler (require API keys)
- NASAGIBS, OpenSeaMap, and 20+ more
A collapsible place search control with geocoding support.
interface SearchControlOptions {
position?: ControlPosition;
visible?: boolean; // Default: true
collapsed?: boolean; // Start collapsed (icon only). Default: true
placeholder?: string; // Search input placeholder. Default: 'Search places...'
geocoderUrl?: string; // Geocoding API URL. Default: Nominatim
maxResults?: number; // Max results to show. Default: 5
debounceMs?: number; // Debounce delay in ms. Default: 300
flyToZoom?: number; // Zoom level when selecting result. Default: 14
showMarker?: boolean; // Show marker at selected location. Default: true
markerColor?: string; // Marker color. Default: '#4264fb'
collapseOnSelect?: boolean; // Collapse after selecting. Default: true
clearOnSelect?: boolean; // Clear results after selecting. Default: true
geocoder?: (query: string) => Promise<SearchResult[]>; // Custom geocoder function
backgroundColor?: string;
borderRadius?: number;
opacity?: number;
width?: number; // Expanded width in pixels. Default: 280
fontSize?: number;
fontColor?: string;
minzoom?: number;
maxzoom?: number;
}
interface SearchResult {
id: string; // Unique identifier
name: string; // Place name
displayName: string; // Full display name with address
lng: number; // Longitude
lat: number; // Latitude
bbox?: [number, number, number, number]; // Bounding box [west, south, east, north]
type?: string; // Place type (city, street, etc.)
importance?: number; // Relevance score
}
// Methods
searchControl.show()
searchControl.hide()
searchControl.expand() // Expand to show input
searchControl.collapse() // Collapse to icon only
searchControl.toggle() // Toggle expanded/collapsed
searchControl.search(query) // Perform a search
searchControl.selectResult(result) // Select a result and fly to it
searchControl.clear() // Clear search and marker
searchControl.update(options)
searchControl.getState()
searchControl.on('resultselect', handler) // Listen for result selection
searchControl.on('search', handler) // Listen for search completion
searchControl.on('clear', handler) // Listen for clear eventsGeocoding: By default, SearchControl uses Nominatim (OpenStreetMap) for geocoding, which is free and requires no API key. You can also provide a custom geocoder function for other services.
// Using custom geocoder
const searchControl = new SearchControl({
geocoder: async (query) => {
const response = await fetch(`https://my-geocoder.com/search?q=${query}`);
const data = await response.json();
return data.map(item => ({
id: item.id,
name: item.name,
displayName: item.address,
lng: item.longitude,
lat: item.latitude,
}));
},
});A control for loading GeoJSON files via file upload button or drag-and-drop.
interface VectorDatasetControlOptions {
position?: ControlPosition;
visible?: boolean; // Default: true
showDropZone?: boolean; // Show overlay when dragging. Default: true
acceptedExtensions?: string[]; // File extensions. Default: ['.geojson', '.json']
multiple?: boolean; // Allow multiple files. Default: true
defaultStyle?: VectorLayerStyle; // Default styling for loaded layers
fitBounds?: boolean; // Fit map to loaded data. Default: true
fitBoundsPadding?: number; // Padding for fitBounds. Default: 50
maxFileSize?: number; // Max file size in bytes. Default: 50MB
backgroundColor?: string;
borderRadius?: number;
opacity?: number;
minzoom?: number;
maxzoom?: number;
}
interface VectorLayerStyle {
fillColor?: string; // Polygon fill. Default: '#3388ff'
fillOpacity?: number; // Polygon fill opacity. Default: 0.3
strokeColor?: string; // Line/outline color. Default: '#3388ff'
strokeWidth?: number; // Line width. Default: 2
strokeOpacity?: number; // Line opacity. Default: 1
circleRadius?: number; // Point radius. Default: 6
circleColor?: string; // Point color. Default: '#3388ff'
circleStrokeColor?: string; // Point outline. Default: '#ffffff'
circleStrokeWidth?: number; // Point outline width. Default: 2
}
interface LoadedDataset {
id: string; // Unique ID
filename: string; // Original filename
sourceId: string; // MapLibre so
85EA
urce ID
layerIds: string[]; // MapLibre layer IDs
featureCount: number; // Number of features
geometryTypes: string[]; // Geometry types present
loadedAt: Date; // When loaded
}
// Methods
vectorControl.show()
vectorControl.hide()
vectorControl.getLoadedDatasets() // Get all loaded datasets
vectorControl.removeDataset(id) // Remove a dataset by ID
vectorControl.removeAllDatasets() // Remove all datasets
vectorControl.loadGeoJSON(geojson, filename) // Programmatically load GeoJSON
vectorControl.update(options)
vectorControl.getState()
vectorControl.on('load', handler) // Fired when a dataset is loaded
vectorControl.on('error', handler) // Fired when an error occursLoading Methods:
- Click the upload button to open a file picker
- Drag and drop GeoJSON files directly onto the map
Supported Formats:
- GeoJSON (.geojson, .json)
- FeatureCollection, Feature, or raw Geometry objects
A toggle control for 3D terrain rendering using free AWS Terrarium elevation tiles.
interface TerrainControlOptions {
sourceUrl?: string; // Terrain tile URL (default: AWS Terrarium)
encoding?: 'terrarium' | 'mapbox'; // Terrain encoding (default: 'terrarium')
exaggeration?: number; // Vertical scale factor (default: 1.0)
enabled?: boolean; // Initial terrain state (default: false)
hillshade?: boolean; // Add hillshade layer (default: true)
hillshadeExaggeration?: number; // Hillshade intensity (default: 0.5)
position?: ControlPosition;
visible?: boolean;
backgroundColor?: string;
borderRadius?: number;
opacity?: number;
minzoom?: number; // Min zoom level to show (default: 0)
maxzoom?: number; // Max zoom level to show (default: 24)
}
// Methods
terrainControl.show()
terrainControl.hide()
terrainControl.enable() // Enable terrain
terrainControl.disable() // Disable terrain
terrainControl.toggle() // Toggle terrain on/off
terrainControl.isEnabled() // Check if terrain is enabled
terrainControl.setExaggeration(value) // Set vertical exaggeration (0.1 - 10.0)
terrainControl.getExaggeration() // Get current exaggeration
terrainControl.enableHillshade() // Enable hillshade layer
terrainControl.disableHillshade() // Disable hillshade layer
terrainControl.toggleHillshade() // Toggle hillshade layer
terrainControl.update(options)
terrainControl.getState()
terrainControl.on('terrainchange', handler) // Listen for terrain toggleTerrain Source: The control uses free terrain tiles from AWS:
- URL:
https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png - Encoding: Terrarium RGB-encoded elevation data
- No API key required
A control for inspecting vector features on the map. Click on features to view their properties/attributes in a popup.
interface InspectControlOptions {
position?: ControlPosition;
visible?: boolean; // Default: true
enabled?: boolean; // Start with inspect mode on. Default: false
maxFeatures?: number; // Max features at click point. Default: 10
includeLayers?: string[]; // Only inspect these layers
excludeLayers?: string[]; // Skip these layers
highlightStyle?: InspectHighlightStyle; // Style for selected feature
excludeProperties?: string[]; // Properties to hide (e.g., internal IDs)
showGeometryType?: boolean; // Show geometry type badge. Default: true
showLayerName?: boolean; // Show layer name. Default: true
maxWidth?: number; // Popup max width. Default: 320
maxHeight?: number; // Popup content max height. Default: 300
backgroundColor?: string;
borderRadius?: number;
opacity?: number;
fontSize?: number;
fontColor?: string;
minzoom?: number;
maxzoom?: number;
}
interface InspectHighlightStyle {
fillColor?: string; // Polygon fill. Default: '#ffff00'
fillOpacity?: number; // Polygon fill opacity. Default: 0.3
strokeColor?: string; // Line/outline color. Default: '#ffff00'
strokeWidth?: number; // Line width. Default: 3
circleRadius?: number; // Point radius. Default: 10
circleStrokeWidth?: number; // Point outline. Default: 3
}
interface InspectedFeature {
id: string; // Unique inspection ID
feature: GeoJSON.Feature; // The GeoJSON feature
layerId: string; // MapLibre layer ID
sourceId: string; // MapLibre source ID
lngLat: [number, number]; // Click coordinates
}
// Methods
inspectControl.show()
inspectControl.hide()
inspectControl.enable() // Enable inspect mode
inspectControl.disable() // Disable inspect mode
inspectControl.toggle() // Toggle inspect mode on/off
inspectControl.isEnabled() // Check if inspect mode is enabled
inspectControl.clear() // Clear current inspection
inspectControl.getInspectedFeatures() // Get all features at click point
inspectControl.getSelectedFeature() // Get currently selected feature
inspectControl.selectFeature(index) // Select feature by index
inspectControl.nextFeature() // Navigate to next feature
inspectControl.previousFeature() // Navigate to previous feature
inspectControl.update(options)
inspectControl.getState()
inspectControl.on('enable', handler) // Fired when inspect mode is enabled
inspectControl.on('disable', handler) // Fired when inspect mode is disabled
inspectControl.on('featureselect', handler) // Fired when a feature is selected
inspectControl.on('clear', handler) // Fired when inspection is clearedUsage:
- Click the info button to enable inspect mode
- Click on any vector feature on the map
- View properties in the popup
- Use < > buttons to navigate when multiple features are at the same location
- Click elsewhere or the button again to disable
viridis- Perceptually uniform, colorblind-friendlyplasma- Perceptually uniforminferno- Perceptually uniformmagma- Perceptually uniformcividis- Colorblind-friendly
coolwarm- Blue to red through whitebwr- Blue-white-redseismic- Blue to redRdBu- Red to blueRdYlBu- Red-yellow-blueRdYlGn- Red-yellow-greenspectral- Rainbow-like diverging
jet- Classic rainbowrainbow- Full spectrumturbo- Improved rainbowterrain- Elevation-likeocean- Ocean depthshot- Black-red-yellow-whitecool- Cyan to magentagray- Grayscalebone- Blue-tinted grayscale
// Use an array of colors
const colorbar = new Colorbar({
colormap: ['#0000ff', '#00ff00', '#ffff00', '#ff0000'],
vmin: 0,
vmax: 100,
});
// Or use color stops for precise control
const colorbar = new Colorbar({
colorStops: [
{ position: 0, color: '#0000ff' },
{ position: 0.3, color: '#00ff00' },
{ position: 0.7, color: '#ffff00' },
{ position: 1, color: '#ff0000' },
],
vmin: 0,
vmax: 100,
});All components support minzoom and maxzoom options to control visibility based on the map's zoom level. This is useful for showing different legends at different zoom levels, similar to how map layers work.
// Show legend only when zoomed in (zoom >= 10)
const detailLegend = new Legend({
title: 'Detailed Features',
items: [...],
minzoom: 10, // Only visible at zoom 10 and above
});
// Show legend only when zoomed out (zoom <= 8)
const overviewLegend = new Legend({
title: 'Overview',
items: [...],
maxzoom: 8, // Only visible at zoom 8 and below
});
// Show colorbar only within a specific zoom range
const colorbar = new Colorbar({
colormap: 'viridis',
vmin: 0,
vmax: 100,
minzoom: 5, // Visible from zoom 5...
maxzoom: 15, // ...up to zoom 15
});<LegendReact
map={map}
title="Lidar Point Cloud"
items={[
{ label: 'QL0 (Approx. <= 0.35m NPS)', color: '#003300' },
{ label: 'QL1 (Approx. 0.35m NPS)', color: '#006600' },
{ label: 'QL2 (Approx. 0.7m NPS)', color: '#00cc00' },
{ label: 'QL3 (Approx. 1.4m NPS)', color: '#ccff00' },
{ label: 'Other', color: '#99ccff' },
]}
minzoom={8}
maxzoom={18}
position="top-left"
/>Note: The visible option takes precedence - if visible is false, the component will be hidden regardless of zoom level.
import { useColorbar, useLegend, useHtmlControl, useBasemap, useTerrain, useSearchControl, useVectorDataset } from 'maplibre-gl
D8D6
-components/react';
function MyComponent() {
const colorbar = useColorbar({ colormap: 'viridis', vmin: 0, vmax: 100 });
const legend = useLegend({ items: [...] });
const htmlControl = useHtmlControl({ html: '...' });
const basemap = useBasemap({ selectedBasemap: 'OpenStreetMap.Mapnik' });
const terrain = useTerrain({ enabled: false, exaggeration: 1.5 });
const search = useSearchControl({ collapsed: true });
const vectorDataset = useVectorDataset();
return (
<>
<button onClick={() => colorbar.setColormap('plasma')}>
Change Colormap
</button>
<button onClick={() => legend.toggle()}>
Toggle Legend
</button>
<button onClick={() => basemap.setBasemap('CartoDB.Positron')}>
Change Basemap
</button>
<button onClick={() => terrain.toggle()}>
Toggle Terrain
</button>
<button onClick={() => search.toggle()}>
Toggle Search
</button>
<SearchControlReact
map={map}
collapsed={search.state.collapsed}
onResultSelect={(result) => search.selectResult(result)}
/>
<TerrainReact
map={map}
enabled={terrain.state.enabled}
exaggeration={terrain.state.exaggeration}
onTerrainChange={(enabled) => terrain.setEnabled(enabled)}
/>
<BasemapReact
map={map}
defaultBasemap={basemap.state.selectedBasemap}
onBasemapChange={(b) => basemap.setBasemap(b.id)}
/>
<ColorbarReact
map={map}
{...colorbar.state}
vmin={colorbar.state.vmin}
vmax={colorbar.state.vmax}
/>
</>
);
}The default styles can be customized using CSS:
/* Override colorbar styles */
.maplibre-gl-colorbar {
background: rgba(0, 0, 0, 0.8);
color: white;
}
/* Override legend styles */
.maplibre-gl-legend {
font-size: 14px;
border-radius: 8px;
}
/* Override HTML control styles */
.maplibre-gl-html-control {
max-width: 400px;
}
/* Override basemap control styles */
.maplibre-gl-basemap {
max-width: 300px;
}
/* Override terrain control styles */
.maplibre-gl-terrain-button {
color: #0078d7;
}
/* Override search control styles */
.maplibre-gl-search {
background: rgba(255, 255, 255, 0.95);
}
.maplibre-gl-search-toggle:hover {
color: #0078d7;
}
/* Override vector dataset control styles */
.maplibre-gl-vector-dataset-button:hover {
color: #0078d7;
}
.maplibre-gl-vector-dataset-dropzone {
background: rgba(0, 120, 215, 0.2);
}See the examples directory for complete working examples:
- Basic Example - Vanilla TypeScript with all three components
- React Example - React with hooks and dynamic updates
# Install dependencies
npm install
# Start development server
npm run dev
# Run tests
npm test
# Build for production
npm run build
# Build examples
npm run build:examplesThe examples can be run using Docker. The image is automatically built and published to GitHub Container Registry.
# Pull the latest image
docker pull ghcr.io/opengeos/maplibre-gl-components:latest
# Run the container
docker run -p 8080:80 ghcr.io/opengeos/maplibre-gl-components:latestThen open http://localhost:8080/maplibre-gl-components/ in your browser to view the examples.
# Build the image
docker build -t maplibre-gl-components .
# Run the container
docker run -p 8080:80 maplibre-gl-components| Tag | Description |
|---|---|
latest |
Latest release |
x.y.z |
Specific version (e.g., 1.0.0) |
x.y |
Minor version (e.g., 1.0) |
MIT License - see LICENSE for details.