map-gl-style-switcher
v0.9.1
Published
A customizable style switcher control for Mapbox GL JS and MapLibre GL JS
Maintainers
Readme
Map GL Style Switcher
A TypeScript control for switching Mapbox GL / MapLibre GL map styles. Easily add a floating style switcher to your map app, with support for multiple styles, images, dark/light themes, and before/after change callbacks.
Available as:
StyleSwitcherControl- Direct IControl implementation for Mapbox/MapLibre GLMapGLStyleSwitcher- React component for react-map-gl integration Live demo of the style switcher control in action
🌐 Live Demo
Animated Demo

Available Styles

Features
- IControl implementation for Mapbox GL / MapLibre GL
- React component for react-map-gl integration
- Floating style switcher in any corner (via map.addControl position)
- Support for multiple map styles with thumbnails
- Expand/collapse on hover with smooth animations
- Dark/light/auto theme support
- RTL text support for Arabic scripts
- Configurable display options (show/hide labels and images)
- Callbacks for before/after style change
- Fully customizable CSS classe's
- TypeScript support
- Accessibility features (ARIA labels, keyboard navigation)
- Comprehensive test coverage
Install
# Using npm (recommended)
npm install map-gl-style-switcher
# Using yarn
yarn add map-gl-style-switcher
# Using pnpm
pnpm add map-gl-style-switcherBasic Map Instance
For React applications where you manage the map instance yourself (e.g., with useEffect), use the useStyleSwitcher hook:
import React, { useEffect, useRef } from 'react';
import * as maplibregl from 'maplibre-gl';
import { StyleSwitcherControl, type StyleItem } from 'map-gl-style-switcher';
// Custom hook
// import { useStyleSwitcher } from 'map-gl-style-switcher/react';
import 'maplibre-gl/dist/maplibre-gl.css';
import 'map-gl-style-switcher/dist/map-gl-style-switcher.css';
const mapStyles: StyleItem[] = [
{
id: 'voyager',
name: 'Voyager',
image:
'https://raw.githubusercontent.com/muimsd/map-gl-style-switcher/refs/heads/main/public/voyager.png',
styleUrl: 'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json',
description: 'Voyager style from Carto',
},
{
id: 'positron',
name: 'Positron',
image:
'https://raw.githubusercontent.com/muimsd/map-gl-style-switcher/refs/heads/main/public/positron.png',
styleUrl: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
description: 'Positron style from Carto',
},
{
id: 'dark-matter',
name: 'Dark Matter',
image:
'https://raw.githubusercontent.com/muimsd/map-gl-style-switcher/refs/heads/main/public/dark.png',
styleUrl: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
description: 'Dark style from Carto',
},
{
id: 'arcgis-hybrid',
name: 'ArcGIS Hybrid',
image:
'https://raw.githubusercontent.com/muimsd/map-gl-style-switcher/refs/heads/main/public/arcgis-hybrid.png',
styleUrl:
'https://raw.githubusercontent.com/go2garret/maps/main/src/assets/json/arcgis_hybrid.json',
description: 'Hybrid Satellite style from ESRI',
},
{
id: 'osm',
name: 'OSM',
image:
'https://raw.githubusercontent.com/muimsd/map-gl-style-switcher/refs/heads/main/public/osm.png',
styleUrl:
'https://raw.githubusercontent.com/go2garret/maps/main/src/assets/json/openStreetMap.json',
description: 'OSM style',
},
];
export default function App() {
const mapContainer = useRef<HTMLDivElement | null>(null)
useEffect(() => {
if (!mapContainer.current) {
console.error('Map container not found!');
return;
}
const currentStyle = mapStyles[0];
const map = new maplibregl.Map({
container: mapContainer.current,
// Use a simple, reliable style first
style: currentStyle.styleUrl,
// Center on Dubai, UAE
center: [55.2708, 25.2048],
zoom: 10,
});
// Add style switcher control
const styleSwitcher = new StyleSwitcherControl({
styles: mapStyles,
theme: 'auto',
showLabels: true,
showImages: true,
activeStyleId: currentStyle.id,
onBeforeStyleChange: (from, to) => {
console.log('Changing style from', from.name, 'to', to.name);
},
onAfterStyleChange: (_from, to) => {
map.setStyle(to.styleUrl);
console.log('Style changed to', to.name);
},
});
map.addControl(styleSwitcher, 'bottom-left');
return () => {
map.remove();
};
}, []); // Empty dependency array
// Add style switcher control to the map
// useStyleSwitcher(map.current, {
// styles,
// theme: 'auto',
// position: 'top-right',
// onAfterStyleChange: (from, to) => {
// if (map.current) {
// map.current.setStyle(to.styleUrl);
// }
// },
// });
return (
<div
ref={mapContainer}
style={{
width: '100%',
height: '500px',
}}
/>
);
}
Examples
React Map GL Example
React Map GL Example
View the react-map-gl example →
Configuration Options
interface StyleSwitcherControlOptions {
styles: StyleItem[]; // Array of map styles (required)
activeStyleId?: string; // Currently active style ID (default: first style)
onBeforeStyleChange?: (from: StyleItem, to: StyleItem) => void; // Callback before style change
onAfterStyleChange?: (from: StyleItem, to: StyleItem) => void; // Callback after style change
showLabels?: boolean; // Show style names (default: true)
showImages?: boolean; // Show style thumbnails (default: true)
animationDuration?: number; // Animation duration in ms (default: 200)
maxHeight?: number; // Max height of expanded list (default: 300)
theme?: 'light' | 'dark' | 'auto'; // UI theme (default: 'light')
classNames?: Partial<StyleSwitcherClassNames>; // Custom CSS classes
rtl?: boolean; // Enable RTL layout (default: false)
}
interface StyleItem {
id: string; // Unique identifier
name: string; // Display name
image: string; // Thumbnail URL or data URI
styleUrl: string; // MapLibre/Mapbox style URL
description?: string; // Optional tooltip text
}
interface StyleSwitcherClassNames {
container: string; // Main container class
list: string; // Expanded list container class
item: string; // Individual style item class
itemSelected: string; // Selected style item class
itemHideLabel: string; // Hide label utility class
dark: string; // Dark theme class
light: string; // Light theme class
}Example of different map styles that can be used with the style switcher
Option Details
activeStyleId: Controls both the initially selected style and what's displayed in the collapsed stateshowLabels&showImages: At least one must betruetheme:'light': Light color scheme'dark': Dark color scheme'auto': Auto-detect from system preference
rtl: Enables right-to-left layout for Arabic/Hebrew interfaces
Customizing CSS Classes
You can override all CSS classes used by the style switcher control using the classNames option:
import { StyleSwitcherControl } from 'map-gl-style-switcher';
const styleSwitcher = new StyleSwitcherControl({
styles,
showLabels: true,
showImages: true,
classNames: {
container: 'my-style-switcher',
list: 'my-style-list',
item: 'my-style-item',
itemSelected: 'my-style-item-selected',
itemHideLabel: 'my-style-item-hide-label',
dark: 'my-style-dark',
light: 'my-style-light',
},
});See the default class names in the StyleSwitcherControl source for all available keys.
Contributing
We welcome contributions! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Development Setup
Clone the repository
git clone https://github.com/muimsd/map-gl-style-switcher cd map-gl-style-switcherInstall dependencies
npm installStart development server
npm run devMake your changes
- Follow TypeScript best practices
- Maintain backward compatibility when possible
- Add tests for new features
Test your changes
npm run validate # Runs type-check, lint, format-check, and testsSubmit a pull request
Guidelines
- Use npm for dependency management
- Follow TypeScript best practices
- Maintain backward compatibility when possible
- Add tests for new features
- Update documentation as needed
- Follow the existing code style
- Ensure all checks pass:
npm run validate
License
MIT License - see LICENSE file for details.
Author
Muhammad Imran Siddique
- GitHub: @muimsd
Repository
- GitHub: https://github.com/muimsd/map-gl-style-switcher
- Issues: https://github.com/muimsd/map-gl-style-switcher/issues
Made with ❤️ for the MapLibre GL and Mapbox GL community
