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

map-gl-offline

v0.5.3

Published

A TypeScript-compatible npm package for MapLibre GL JS that enables comprehensive offline storage and usage of vector/raster tiles, sprites, styles, fonts (glyphs), and entire map regions with advanced analytics, import/export capabilities, and intelligen

Readme

Map GL Offline 🗺️

npm version License: MIT TypeScript

Documentation | Live Demo

A comprehensive TypeScript library for MapLibre GL JS and Mapbox GL JS that enables complete offline map functionality with vector/raster tiles, styles, fonts, sprites, and glyphs stored in IndexedDB. Features include Mapbox Standard style support, advanced analytics, intelligent cleanup, i18n (English & Arabic with RTL), and a modern glassmorphic UI control.

🎬 Demo

Map GL Offline Demo

Download regions, load offline styles, and navigate maps without an internet connection.

✨ Features

🎯 Core Offline Capabilities

  • 🗺️ Complete Offline Maps: Download and store entire map regions with polygon-based selection
  • 🎯 Smart Tile Management: Efficient vector/raster tile downloading, caching, and retrieval with zoom-level optimization
  • 🔤 Font & Glyph Support: Comprehensive font and glyph management with Unicode range support
  • 🎨 Sprite Management: Handle map sprites and icons offline with multi-resolution support (@1x, @2x)
  • 📊 Real-time Analytics: Detailed storage analytics, performance metrics, and optimization recommendations

🌐 Mapbox GL JS Support

  • 🔗 mapbox:// Protocol Resolution: Automatic resolution of mapbox:// style, source, sprite, and glyph URLs
  • 🏙️ Mapbox Standard Style: Full support including 3D models, raster-dem terrain, and import-based style resolution
  • 🌅 Day/Night Light Presets: Toggle between day and night lighting in Mapbox Standard style
  • 🌧️ Weather Controls: Rain and snow effects for Mapbox Standard style
  • 🔍 Auto-detection: Automatically detects whether a style is Mapbox or MapLibre and applies the correct handling

🎨 Modern UI Control

  • 🖼️ Glassmorphic Design: Beautiful modern interface with glassmorphism effects and smooth animations
  • 🌓 Dark/Light Theme: Automatic theme switching with system preference detection
  • 📍 Polygon Drawing: Interactive polygon tool for precise region selection
  • 📊 Live Progress: Real-time download progress with detailed statistics
  • 🎯 Region Management: Easy-to-use interface for managing multiple offline regions
  • Responsive: Mobile-friendly design that adapts to all screen sizes
  • 🌍 Internationalization: English and Arabic language support with full RTL layout

🛠️ Technical Excellence

  • 💾 IndexedDB Storage: Efficient browser storage with quota management and transaction safety
  • 🔧 Full TypeScript: Complete type definitions, interfaces, and compile-time safety
  • Performance Optimized: Concurrent downloads, async/await patterns, and memory-efficient operations
  • 🧹 Intelligent Cleanup: Smart cleanup of expired data with customizable policies
  • 🔄 Robust Error Handling: Comprehensive error recovery, retry mechanisms, and graceful degradation
  • 🔍 Enhanced Logging: Detailed debugging with zoom-level specific logging (Z12 tracking)

📦 Installation

npm install map-gl-offline
# or
yarn add map-gl-offline
# or
pnpm add map-gl-offline

CDN (UMD)

For use via <script> tag, the library is available as the mapgloffline global (similar to mapboxgl and maplibregl):

<script src="https://unpkg.com/map-gl-offline/dist/index.umd.js"></script>
<link rel="stylesheet" href="https://unpkg.com/map-gl-offline/style.css" />
<script>
  const manager = new mapgloffline.OfflineMapManager();
  const control = new mapgloffline.OfflineManagerControl(manager, {
    styleUrl: 'https://api.maptiler.com/maps/streets/style.json?key=YOUR_KEY',
  });
  map.addControl(control, 'top-right');
</script>

🔑 Environment Setup

For development or when using Maptiler styles, create a .env file:

VITE_MAPTILER_API_KEY=your_api_key_here

Get a free API key from Maptiler.

For Mapbox styles, you will also need a Mapbox access token from Mapbox.

🚀 Quick Start

MapLibre GL JS

import maplibregl from 'maplibre-gl';
import { OfflineMapManager, OfflineManagerControl } from 'map-gl-offline';
import 'maplibre-gl/dist/maplibre-gl.css';
import 'map-gl-offline/style.css';

const styleUrl = 'https://api.maptiler.com/maps/streets/style.json?key=YOUR_API_KEY';

const map = new maplibregl.Map({
  container: 'map',
  style: styleUrl,
  center: [-74.006, 40.7128],
  zoom: 12,
});

const offlineManager = new OfflineMapManager();

map.on('load', () => {
  const control = new OfflineManagerControl(offlineManager, {
    styleUrl,
    theme: 'dark',
    showBbox: true,
    mapLib: maplibregl, // enables idb:// protocol in web workers
  });
  map.addControl(control, 'top-right');
});

Mapbox GL JS

Mapbox GL JS v3 does not support addProtocol, so offline tile serving uses a Service Worker fallback. You need to copy idb-offline-sw.js to your project's public directory so it is served at the root (/idb-offline-sw.js).

Option 1: CLI (recommended)

npx map-gl-offline init

Option 2: Vite plugin

// vite.config.js
import { offlineSwPlugin } from 'map-gl-offline/vite-plugin';

export default defineConfig({
  plugins: [offlineSwPlugin()],
});

Option 3: Manual copy

cp node_modules/map-gl-offline/dist/idb-offline-sw.js public/idb-offline-sw.js
import mapboxgl from 'mapbox-gl';
import { OfflineMapManager, OfflineManagerControl } from 'map-gl-offline';
import 'mapbox-gl/dist/mapbox-gl.css';
import 'map-gl-offline/style.css';

mapboxgl.accessToken = 'YOUR_MAPBOX_TOKEN';

const map = new mapboxgl.Map({
  container: 'map',
  style: 'mapbox://styles/mapbox/standard',
  center: [-74.006, 40.7128],
  zoom: 12,
});

const offlineManager = new OfflineMapManager();

map.on('load', () => {
  const control = new OfflineManagerControl(offlineManager, {
    styleUrl: 'mapbox://styles/mapbox/standard',
    theme: 'dark',
    showBbox: true,
    accessToken: mapboxgl.accessToken,
    // No mapLib needed - Mapbox GL JS v3 lacks addProtocol,
    // so the library auto-registers a Service Worker fallback
  });
  map.addControl(control, 'top-right');
});

Note: MapLibre GL JS has built-in addProtocol support, so it does not need the Service Worker. Only Mapbox GL JS requires this extra step.

The UI control provides:

  • 📍 Polygon drawing for region selection
  • 📊 Download progress tracking
  • 🗂️ Region management (view, delete)
  • 🌓 Theme toggle (dark/light mode)
  • 📈 Storage analytics
  • 🌍 Language switcher (English / Arabic with RTL)

Programmatic Usage

import { OfflineMapManager } from 'map-gl-offline';

// Initialize the offline manager
const offlineManager = new OfflineMapManager();

// Download a map region for offline use
await offlineManager.addRegion({
  id: 'downtown',
  name: 'Downtown Area',
  bounds: [
    [-74.0559, 40.7128], // Southwest [lng, lat]
    [-74.0059, 40.7628], // Northeast [lng, lat]
  ],
  minZoom: 10,
  maxZoom: 16,
  styleUrl: 'https://api.maptiler.com/maps/streets/style.json?key=YOUR_KEY',
  onProgress: progress => {
    console.log(`Progress: ${progress.percentage}%`);
    console.log(`Current: ${progress.message}`);
  },
});

// Retrieve a stored region
const region = await offlineManager.getStoredRegion('downtown');
if (region) {
  console.log(`Region: ${region.name}, created: ${new Date(region.created).toLocaleDateString()}`);
}

// List all regions
const regions = await offlineManager.listStoredRegions();
console.log(`Stored regions:`, regions);

// Delete a region
await offlineManager.deleteRegion('downtown');

Analytics & Monitoring

// Get comprehensive storage analytics
const analytics = await offlineManager.getComprehensiveStorageAnalytics();
console.log(`Total storage: ${analytics.totalStorageSize} bytes`);
console.log(`Tiles: ${analytics.tiles.count} (${analytics.tiles.totalSize} bytes)`);
console.log(`Fonts: ${analytics.fonts.count} (${analytics.fonts.totalSize} bytes)`);
console.log(`Sprites: ${analytics.sprites.count} (${analytics.sprites.totalSize} bytes)`);
console.log(`Recommendations:`, analytics.recommendations);

Cleanup & Maintenance

// Clean up expired regions
const deletedCount = await offlineManager.cleanupExpiredRegions();
console.log(`Cleaned ${deletedCount} expired regions`);

// Verify and repair fonts
const verification = await offlineManager.verifyAndRepairFonts('style_123', { removeCorrupted: true });
console.log(`Verified: ${verification.verified}, Repaired: ${verification.repaired}, Removed: ${verification.removed}`);

// Set up automatic cleanup (runs every 24 hours)
const cleanupId = await offlineManager.setupAutoCleanup({
  intervalHours: 24,
  maxAge: 30, // days
});

📚 API Reference

OfflineMapManager

Main class for managing offline maps.

Constructor:

const manager = new OfflineMapManager(overrides?: OfflineManagerServiceOverrides);

The constructor accepts optional service overrides for dependency injection (advanced usage). For most cases, use the default: new OfflineMapManager().

Core Methods:

  • addRegion(options: OfflineRegionOptions) - Download and store a map region
  • getStoredRegion(id: string) - Retrieve a stored region by ID
  • deleteRegion(id: string) - Delete a specific region and its resources
  • listStoredRegions() - List all stored regions with metadata
  • listRegions() - List all region options

Analytics Methods:

  • getComprehensiveStorageAnalytics() - Get detailed storage statistics
  • getRegionAnalytics() - Get aggregate analytics across all regions
  • getTileStatistics(styleId: string) - Get tile-specific statistics
  • getFontStatistics(styleId: string) - Get font statistics
  • getSpriteStatistics(styleId: string) - Get sprite statistics

Cleanup & Maintenance Methods:

  • cleanupExpiredRegions() - Remove regions past expiration date
  • performSmartCleanup(options) - Intelligent cleanup with configurable criteria
  • cleanupOldFonts(styleId?, options?) - Remove old font data
  • cleanupOldSprites(styleId?, options?) - Remove old sprite data
  • cleanupOldGlyphs(styleId?, options?) - Remove old glyph data
  • verifyAndRepairFonts(styleId, options?) - Verify font integrity
  • verifyAndRepairSprites(styleId, options?) - Verify sprite integrity
  • verifyAndRepairGlyphs(styleId, options?) - Verify glyph integrity
  • setupAutoCleanup(options) - Enable automatic periodic cleanup
  • stopAutoCleanup(cleanupId?) - Disable a specific auto-cleanup
  • stopAllAutoCleanup() - Disable all auto-cleanups
  • performCompleteMaintenance(options?) - Run comprehensive maintenance

OfflineManagerControl

UI control for MapLibre GL JS and Mapbox GL JS with glassmorphic design.

Constructor:

const offlineManager = new OfflineMapManager();

const control = new OfflineManagerControl(offlineManager, {
  styleUrl: 'https://example.com/style.json', // Map style URL (required)
  theme?: 'light' | 'dark',                   // UI theme (default: 'dark')
  showBbox?: boolean,                          // Show region bounding boxes (default: false)
  accessToken?: string,                        // Mapbox access token (for mapbox:// URLs)
  mapLib?: MapLibProtocol,                     // Map library module (e.g. maplibregl) for idb:// protocol
});

Features:

  • Interactive polygon drawing for region selection
  • Real-time download progress tracking
  • Region management (view, delete)
  • Theme toggle (dark/light mode)
  • Storage analytics display
  • Language switcher (English / Arabic with RTL support)
  • Responsive mobile-friendly design

🔧 Configuration Options

OfflineRegionOptions

interface OfflineRegionOptions {
  id: string; // Unique region identifier
  name: string; // Human-readable name (required)
  bounds: [[number, number], [number, number]]; // [[lng, lat], [lng, lat]]
  minZoom: number; // Minimum zoom level (e.g., 10)
  maxZoom: number; // Maximum zoom level (e.g., 16)
  styleUrl?: string; // Map style URL
  expiry?: number; // Expiration timestamp (ms since epoch)
  deleteOnExpiry?: boolean; // Auto-delete on expiration
  multipleRegions?: boolean; // Part of a multi-region download
  tileExtension?: string; // Tile extension (pbf, mvt, png, etc.)
}

🎯 Use Cases

  • 🏔️ Outdoor & Recreation Apps: Hiking, camping, and adventure apps with offline trail maps
  • 📱 Field Data Collection: Survey and data collection in remote areas
  • 🚨 Emergency Response: Critical map access during network outages
  • ✈️ Travel Apps: Tourist apps with offline city maps
  • 🚗 Fleet Management: Vehicle tracking with offline map fallback
  • 📊 Asset Management: Field service apps with offline capability
  • 🎓 Educational Apps: Geography and learning apps with downloadable maps
  • 🏗️ Construction & Engineering: Site management with offline blueprints
  • 💾 Bandwidth Optimization: Reduce data costs by pre-downloading maps

💡 Best Practices

Performance Optimization

// Balance quality vs storage with appropriate zoom levels
const region = {
  minZoom: 10, // Don't go too low (tile count grows exponentially)
  maxZoom: 16, // Don't go too high (diminishing returns)
  bounds: [
    /* ... */
  ],
};

// Monitor storage usage
const analytics = await manager.getComprehensiveStorageAnalytics();
if (analytics.totalStorageSize > 500 * 1024 * 1024) {
  console.warn('High storage usage detected');
  await manager.performSmartCleanup({ maxStorageSize: 500 * 1024 * 1024 });
}

// Use progressive loading for better UX
const progressiveDownload = {
  priorityZoomLevels: [12, 13, 11, 14, 10, 15, 16],
  onProgress: p => updateUI(p),
};

Error Handling

try {
  await manager.addRegion(regionOptions);
} catch (error) {
  if (error.message.includes('quota')) {
    console.error('Storage quota exceeded');
    await manager.cleanupExpiredRegions();
  } else if (error.message.includes('network')) {
    console.error('Network error. Retrying...');
  } else {
    console.error('Unexpected error:', error);
  }
}

Storage Management

// Check available storage
if ('storage' in navigator && 'estimate' in navigator.storage) {
  const { usage, quota } = await navigator.storage.estimate();
  console.log(`Used: ${usage} / ${quota} bytes`);
}

// Regular cleanup
await manager.cleanupExpiredRegions();

// Auto-cleanup on startup
await manager.setupAutoCleanup({
  intervalHours: 24, // Daily
  maxAge: 30, // 30 days
});

🔍 Troubleshooting

Storage Quota Issues

// Check quota
const { usage, quota } = await navigator.storage.estimate();
if (usage / quota > 0.9) {
  await manager.cleanupExpiredRegions();
}

// Request persistent storage
if (navigator.storage?.persist) {
  const isPersisted = await navigator.storage.persist();
  console.log(`Persistent storage: ${isPersisted}`);
}

Performance Issues

// Reduce concurrency for slower devices
const lightOptions = {
  maxConcurrency: 2,
  batchSize: 10,
  timeout: 30000,
};

// Use smaller regions
const smallerRegion = {
  minZoom: 11, // Start at higher zoom
  maxZoom: 15, // End at lower zoom
};

🌐 Browser Compatibility

| Browser | Version | Support | | ------- | ------- | ------- | | Chrome | 51+ | ✅ | | Firefox | 45+ | ✅ | | Safari | 10+ | ✅ | | Edge | 79+ | ✅ | | Mobile | Modern | ✅ |

Requirements:

  • IndexedDB support
  • ES2015+ JavaScript
  • Async/await support
  • Web Workers (optional, for background tasks)

🤝 Contributing

Contributions are welcome! Please see our Contributing Guide for details.

Development Setup

# Clone repository
git clone https://github.com/muimsd/map-gl-offline.git
cd map-gl-offline

# Install dependencies
npm install

# Run development server
npm run dev

# Run tests
npm test

# Build library
npm run build

# Run MapLibre example app
cd examples/maplibre
npm install
npm run dev

Project Structure

map-gl-offline/
├── src/
│   ├── managers/          # Core offline manager
│   ├── services/          # Tile, font, sprite services
│   ├── storage/           # IndexedDB management
│   ├── ui/                # UI components & controls
│   │   └── translations/  # i18n (English, Arabic)
│   ├── utils/             # Utilities & helpers
│   └── types/             # TypeScript definitions
├── bin/                   # CLI (map-gl-offline init) & Vite plugin
├── examples/
│   ├── maplibre/          # MapLibre GL JS example app
│   └── mapbox-gl/         # Mapbox GL JS example app
├── docs/                  # Docusaurus documentation site
└── tests/                 # Test suites

📞 Support & Links

🔄 Recent Updates

v0.5.3 (Latest)

  • Bundle Size: Reduced ESM bundle from 783 KB to 565 KB (28% reduction)
  • Turf Tree-Shaking: Replaced @turf/turf monorepo with individual packages (@turf/area, @turf/bbox-polygon, @turf/difference, @turf/helpers)
  • Externalized Dependencies: i18next and @turf/* moved to externals
  • Removed Unused Dependency: Removed @tabler/icons (unused, 47 MB install)
  • Bug Fixes: 20+ bugs resolved from comprehensive codebase audit
  • Import Atomicity: Region imports use single IndexedDB transactions
  • Expired Region Cleanup: forceCleanupExpiredRegions now uses actual expiry timestamps
  • XSS Prevention: User data escaped in all UI templates
  • Code Quality: Dead code removal, @/ path alias for all imports

v0.5.2

  • CLI Command: npx map-gl-offline init to copy the Service Worker into your project
  • Vite Plugin: offlineSwPlugin() to auto-copy the Service Worker on each build
  • Mapbox GL Example: Full React + Vite example app for Mapbox GL JS

v0.5.0

  • Mapbox GL JS Support: Full support for Mapbox styles, including mapbox:// protocol URL resolution
  • Mapbox Standard Style: 3D models, raster-dem terrain, and import-based style resolution
  • Day/Night Light Presets: Toggle between day and night lighting for Mapbox Standard
  • Rain & Snow Weather: Weather effect controls for Mapbox Standard style
  • Import Resolver: Automatic resolution of Mapbox Standard imports in styles
  • Internationalization: English and Arabic language support with full RTL layout
  • Auto-detection: Automatically detects Mapbox vs MapLibre styles

v0.1.0

  • Fractional Zoom Fix: Fixed tile loading at fractional zoom levels
  • Modern UI: Glassmorphic design with dark/light theme
  • Polygon Drawing: Interactive region selection tool
  • Enhanced Analytics: Comprehensive storage insights
  • Performance: Optimized downloads and memory usage
  • TypeScript: Full type safety throughout

See CHANGELOG.md for complete version history.

🙏 Acknowledgments

📄 License

MIT © Muhammad Imran Siddique


Made with ❤️ for the mapping community

📖 Documentation🎮 Live Demo⭐ Star on GitHub