npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@deciosfernandes/mapbox-v3-gl-style-switcher

v1.2.1

Published

A modern, fully-typed TypeScript control for Mapbox GL JS with dropdown-style interface for switching between map styles

Readme

Mapbox GL JS Style Switcher

A modern, fully-typed TypeScript control for Mapbox GL JS that provides an intuitive dropdown-style interface for switching between map styles.

Bundle Size npm version TypeScript License

✨ Features

  • 🎨 Pre-configured styles - Comes with 5 popular Mapbox styles out of the box
  • 🔧 Fully customizable - Use your own styles and configurations
  • 🎯 TypeScript first - Built with modern TypeScript, full type safety
  • 📱 Responsive design - Works seamlessly on desktop and mobile
  • 🎪 Event-driven - Rich event system for custom interactions
  • 🚀 Modern syntax - Leverages optional chaining and nullish coalescing
  • 🛡️ Error resilient - Robust error handling and validation
  • 📦 Zero dependencies - Only requires Mapbox GL JS

🚀 Installation

npm i @deciosfernandes/mapbox-v3-gl-style-switcher --save
yarn add @deciosfernandes/mapbox-v3-gl-style-switcher
pnpm add @deciosfernandes/mapbox-v3-gl-style-switcher

📋 Requirements

  • Mapbox GL JS v3.x
  • TypeScript 5.0+ (for TypeScript projects)
  • Node.js 16+ (for development)

🎯 Quick Start

import { MapboxStyleSwitcherControl } from '@deciosfernandes/mapbox-v3-gl-style-switcher';
import { Map as MapboxMap } from 'mapbox-gl';

// Import the CSS styles
import '@deciosfernandes/mapbox-v3-gl-style-switcher/styles.css';

// Create your map
const map = new MapboxMap({
    container: 'map',
    accessToken: 'your-mapbox-token',
    style: 'mapbox://styles/mapbox/streets-v12',
    center: [-74.5, 40],
    zoom: 9,
});

// Add the style switcher control
map.addControl(new MapboxStyleSwitcherControl());

🎨 Default Styles

The control includes these pre-configured Mapbox styles:

| Style Name | Description | URI | | ------------- | ------------------------------ | ---------------------------------------------- | | Streets | Standard street map (default) | mapbox://styles/mapbox/streets-v12 | | Light | Clean, minimal light theme | mapbox://styles/mapbox/light-v11 | | Dark | Elegant dark theme | mapbox://styles/mapbox/dark-v11 | | Outdoors | Perfect for outdoor activities | mapbox://styles/mapbox/outdoors-v12 | | Satellite | Satellite imagery with streets | mapbox://styles/mapbox/satellite-streets-v12 |

🔧 Configuration

Custom Styles

import { MapboxStyleDefinition, MapboxStyleSwitcherControl } from '@deciosfernandes/mapbox-v3-gl-style-switcher';

const customStyles: MapboxStyleDefinition[] = [
    {
        title: 'Custom Dark',
        uri: 'mapbox://styles/your-username/your-dark-style-id',
    },
    {
        title: 'Custom Light',
        uri: 'mapbox://styles/your-username/your-light-style-id',
    },
    {
        title: 'Vintage',
        uri: 'mapbox://styles/your-username/vintage-style-id',
    },
];

map.addControl(new MapboxStyleSwitcherControl(customStyles));

Advanced Configuration

import { MapboxStyleSwitcherOptions } from '@deciosfernandes/mapbox-v3-gl-style-switcher';

const options: MapboxStyleSwitcherOptions = {
    defaultStyle: 'Custom Dark',
    eventListeners: {
        onOpen: (event) => {
            console.log('Style selector opened');
            // Return true to prevent default behavior
            return false;
        },
        onSelect: (event) => {
            console.log('Style button clicked');
            return false;
        },
        onChange: (event, styleUri) => {
            console.log(`Map style changed to: ${styleUri}`);
            // Analytics tracking example
            analytics.track('style_changed', { style: styleUri });
        },
    },
};

map.addControl(new MapboxStyleSwitcherControl(customStyles, options));

Backward Compatible Syntax

// Simple string-based default style (legacy support)
map.addControl(new MapboxStyleSwitcherControl(customStyles, 'Dark'));

📖 API Reference

MapboxStyleSwitcherControl

Constructor

constructor(
    styles?: MapboxStyleDefinition[],
    options?: MapboxStyleSwitcherOptions | string
)

Parameters:

  • styles - Optional array of custom style definitions. Defaults to built-in Mapbox styles
  • options - Configuration options object, or string for backward compatibility (default style name)

Public Methods

getCurrentStyle(): MapboxStyleDefinition | null

Returns the currently active style definition.

const control = new MapboxStyleSwitcherControl();
map.addControl(control);

// Later in your code
const currentStyle = control.getCurrentStyle();
console.log(`Current style: ${currentStyle?.title}`);
setStyle(styleName: string): boolean

Programmatically changes the map style.

const success = control.setStyle('Dark');
if (success) {
    console.log('Style changed successfully');
} else {
    console.log('Style not found or change failed');
}
getStyles(): ReadonlyArray<MapboxStyleDefinition>

Returns all available style definitions.

const availableStyles = control.getStyles();
console.log(
    'Available styles:',
    availableStyles.map((s) => s.title),
);
getDefaultPosition(): ControlPosition

Returns the default control position ('top-right').

Interfaces

MapboxStyleDefinition

interface MapboxStyleDefinition {
    /** Display title for the style */
    title: string;
    /** Mapbox style URI */
    uri: string;
}

MapboxStyleSwitcherOptions

interface MapboxStyleSwitcherOptions {
    /** Default style to be selected on initialization */
    defaultStyle?: string;
    /** Event listeners for style switcher interactions */
    eventListeners?: MapboxStyleSwitcherEvents;
}

MapboxStyleSwitcherEvents

interface MapboxStyleSwitcherEvents {
    /** Fired when the style selector is opened */
    onOpen?: (event: MouseEvent) => boolean;
    /** Fired when a style button is clicked */
    onSelect?: (event: MouseEvent) => boolean;
    /** Fired after the map style is changed */
    onChange?: (event: MouseEvent, style: string) => void;
}

Event Handler Return Values (onOpen, onSelect):

  • Return true to prevent the default behavior
  • Return false or undefined to allow normal processing

onChange fires after the style has already changed and has no return value.

💡 Advanced Examples

Dynamic Style Management

class MapManager {
    private map: MapboxMap;
    private styleControl: MapboxStyleSwitcherControl;

    constructor() {
        this.map = new MapboxMap({
            /* config */
        });
        this.styleControl = new MapboxStyleSwitcherControl(this.getCustomStyles(), {
            defaultStyle: 'Corporate',
            eventListeners: {
                onChange: this.handleStyleChange.bind(this),
            },
        });

        this.map.addControl(this.styleControl);
    }

    private getCustomStyles(): MapboxStyleDefinition[] {
        return [
            { title: 'Corporate', uri: 'mapbox://styles/company/corporate' },
            { title: 'Presentation', uri: 'mapbox://styles/company/presentation' },
            { title: 'Analysis', uri: 'mapbox://styles/company/analysis' },
        ];
    }

    private handleStyleChange(event: MouseEvent, styleUri: string): void {
        // Custom logic when style changes
        this.updateUI(styleUri);
        this.saveUserPreference(styleUri);
    }

    public switchToAnalysisMode(): void {
        this.styleControl.setStyle('Analysis');
    }
}

Integration with UI Frameworks

React Example

import React, { useEffect, useRef } from 'react';
import mapboxgl from 'mapbox-gl';
import { MapboxStyleSwitcherControl } from '@deciosfernandes/mapbox-v3-gl-style-switcher';

const MapComponent: React.FC = () => {
    const mapContainer = useRef<HTMLDivElement>(null);
    const map = useRef<mapboxgl.Map | null>(null);

    useEffect(() => {
        if (!mapContainer.current) return;

        map.current = new mapboxgl.Map({
            container: mapContainer.current,
            style: 'mapbox://styles/mapbox/streets-v12',
            center: [-74.5, 40],
            zoom: 9,
        });

        const styleControl = new MapboxStyleSwitcherControl(undefined, {
            defaultStyle: 'Streets',
            eventListeners: {
                onChange: (event, style) => {
                    console.log('React: Style changed to', style);
                },
            },
        });

        map.current.addControl(styleControl);

        return () => {
            map.current?.remove();
        };
    }, []);

    return (
        <div
            ref={mapContainer}
            style={{ width: '100%', height: '400px' }}
        />
    );
};

export default MapComponent;

Vue Example

<template>
    <div
        ref="mapContainer"
        class="map-container"></div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import mapboxgl from 'mapbox-gl';
import { MapboxStyleSwitcherControl } from '@deciosfernandes/mapbox-v3-gl-style-switcher';

const mapContainer = ref<HTMLDivElement>();
let map: mapboxgl.Map | null = null;

onMounted(() => {
    if (!mapContainer.value) return;

    map = new mapboxgl.Map({
        container: mapContainer.value,
        style: 'mapbox://styles/mapbox/streets-v12',
        center: [-74.5, 40],
        zoom: 9,
    });

    const styleControl = new MapboxStyleSwitcherControl();
    map.addControl(styleControl);
});

onUnmounted(() => {
    map?.remove();
});
</script>

<style scoped>
.map-container {
    width: 100%;
    height: 400px;
}
</style>

🎨 Styling & CSS

The control uses CSS classes that can be customized:

/* Main control container */
.mapboxgl-ctrl.mapboxgl-ctrl-group {
    /* Your custom styles */
}

/* Style switcher button */
.mapboxgl-style-switcher {
    /* Button styles */
}

/* Style list dropdown */
.mapboxgl-style-list {
    /* Dropdown styles */
}

/* Individual style buttons */
.mapboxgl-style-list button {
    /* Style button styles */
}

/* Active style button */
.mapboxgl-style-list button.active {
    /* Active state styles */
}

🛠️ Development

Setup

git clone https://github.com/deciosfernandes/style-switcher.git
cd style-switcher
npm install

Build

npm run build

Development Workflow

The project uses modern TypeScript 5.9.3 with:

  • Optional chaining and nullish coalescing
  • Strict type checking
  • ES2020 target with full ES2020 library features
  • Comprehensive JSDoc documentation

🤝 Contributing

Contributions are welcome! Please read our contributing guidelines:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Commit your changes: git commit -m 'Add amazing feature'
  4. Push to the branch: git push origin feature/amazing-feature
  5. Open a Pull Request

Development Guidelines

  • Follow TypeScript strict mode conventions
  • Add JSDoc comments for public APIs
  • Include unit tests for new features
  • Update documentation for API changes
  • Ensure backward compatibility when possible

📄 License

This project is licensed under the GPL-3.0 License - see the LICENSE file for details.

🙏 Acknowledgments

  • Original concept by Eliz Kilic
  • Built for the Mapbox GL JS ecosystem
  • TypeScript definitions inspired by the Mapbox GL JS type definitions

Made with ❤️ for the Mapbox community