A template for creating MapLibre GL JS plugins with TypeScript and React support.
- TypeScript Support - Full TypeScript support with type definitions
- React Integration - React wrapper component and custom hooks
- IControl Implementation - Implements MapLibre's IControl interface
- Modern Build Setup - Vite-based build with dual ESM/CJS output
- Testing - Vitest setup with React Testing Library
- CI/CD Ready - GitHub Actions for npm publishing and GitHub Pages
npm install maplibre-gl-plugin-templateimport maplibregl from 'maplibre-gl';
import { PluginControl } from 'maplibre-gl-plugin-template';
import 'maplibre-gl-plugin-template/style.css';
const map = new maplibregl.Map({
container: 'map',
style: 'https://demotiles.maplibre.org/style.json',
center: [0, 0],
zoom: 2,
});
map.on('load', () => {
const control = new PluginControl({
title: 'My Plugin',
collapsed: false,
panelWidth: 300,
});
map.addControl(control, 'top-right');
});import { useEffect, useRef, useState } from 'react';
import maplibregl, { Map } from 'maplibre-gl';
import { PluginControlReact, usePluginState } from 'maplibre-gl-plugin-template/react';
import 'maplibre-gl-plugin-template/style.css';
function App() {
const mapContainer = useRef<HTMLDivElement>(null);
const [map, setMap] = useState<Map |
8000
span> null>(null);
const { state, toggle } = usePluginState();
useEffect(() => {
if (!mapContainer.current) return;
const mapInstance = new maplibregl.Map({
container: mapContainer.current,
style: 'https://demotiles.maplibre.org/style.json',
center: [0, 0],
zoom: 2,
});
mapInstance.on('load', () => setMap(mapInstance));
return () => mapInstance.remove();
}, []);
return (
<div style={{ width: '100%', height: '100vh' }}>
<div ref={mapContainer} style={{ width: '100%', height: '100%' }} />
{map && (
<PluginControlReact
map={map}
title="My Plugin"
collapsed={state.collapsed}
onStateChange={(newState) => console.log(newState)}
/>
)}
</div>
);
}The main control class implementing MapLibre's IControl interface.
| Option | Type | Default | Description |
|---|---|---|---|
collapsed |
boolean |
true |
Whether the panel starts collapsed (showing only the 29x29 toggle button) |
position |
string |
'top-right' |
Control position on the map |
title |
string |
'Plugin Control' |
Title displayed in the header |
panelWidth |
number |
300 |
Width of the dropdown panel in pixels |
className |
string |
'' |
Custom CSS class name |
toggle()- Toggle the collapsed stateexpand()- Expand the panelcollapse()- Collapse the panelgetState()- Get the current statesetState(state)- Update the stateon(event, handler)- Register an event handleroff(event, handler)- Remove an event handlergetMap()- Get the map instancegetContainer()- Get the container element
collapse- Fired when the panel is collapsedexpand- Fired when the panel is expandedstatechange- Fired when the state changes
React wrapper component for PluginControl.
All PluginControl options plus:
| Prop | Type | Description |
|---|---|---|
map |
Map |
MapLibre GL map instance (required) |
onStateChange |
function |
Callback fired when state changes |
Custom React hook for managing plugin state.
const {
state, // Current state
setState, // Update entire state
setCollapsed, // Set collapsed state
setPanelWidth,// Set panel width
setData, // Set custom data
reset, // Reset to initial state
toggle, // Toggle collapsed state
} = usePluginState(initialState);The package exports several utility functions:
clamp(value, min, max)- Clamp a value between min and maxformatNumericValue(value, step)- Format a number with appropriate decimalsgenerateId(prefix?)- Generate a unique IDdebounce(fn, delay)- Debounce a functionthrottle(fn, limit)- Throttle a functionclassNames(classes)- Build a class string from an object
# Clone the repository
git clone https://github.com/your-username/maplibre-gl-plugin-template.git
cd maplibre-gl-plugin-template
# Install dependencies
npm install
# Start development server
npm run dev| Script | Description |
|---|---|
npm run dev |
Start development server |
npm run build |
Build the library |
npm run build:examples |
Build examples for deployment |
npm run test |
Run tests |
npm run test:ui |
Run tests with UI |
npm run test:coverage |
Run tests with coverage |
npm run lint |
Lint the code |
npm run format |
Format the code |
maplibre-gl-plugin-template/
├── src/
│ ├── index.ts # Main entry point
│ ├── react.ts # React entry point
│ ├── index.css # Root styles
│ └── lib/
│ ├── core/ # Core classes and types
│ ├── hooks/ # React hooks
│ ├── utils/ # Utility functions
│ └── styles/ # Component styles
├── tests/ # Test files
├── examples/ # Example applications
│ ├── basic/ # Vanilla JS example
│ └── react/ # React example
└── .github/workflows/ # CI/CD workflows
The 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-plugin-template:latest
# Run the container
docker run -p 8080:80 ghcr.io/opengeos/maplibre-gl-plugin-template:latestThen open http://localhost:8080/maplibre-gl-plugin-template/ in your browser to view the examples.
# Build the image
docker build -t maplibre-gl-plugin-template .
# Run the container
docker run -p 8080:80 maplibre-gl-plugin-template| Tag | Description |
|---|---|
latest |
Latest release |
x.y.z |
Specific version (e.g., 1.0.0) |
x.y |
Minor version (e.g., 1.0) |
To use this template for your own plugin:
- Clone or fork this repository
- Update
package.jsonwith your plugin name and details - Modify
src/lib/core/PluginControl.tsto implement your plugin logic - Update the styles in
src/lib/styles/plugin-control.css - Add custom utilities, hooks, or components as needed
- Update the README with your plugin's documentation
MIT License - see LICENSE for details.