@salla.sa/ui-google-map-render
v1.2.1
Published
Framework-agnostic Google Maps web components using Lit - lightweight with runtime library loading
Readme
@salla.sa/ui-google-map-render
Framework-agnostic Google Maps web components built with Lit. Modern, accessible, and easy to use in any web project.
Features
- ✅ Lightweight: Only ~17KB gzipped - Google Maps loaded at runtime from CDN
- ✅ Framework Agnostic: Works with React, Vue, Angular, or vanilla JavaScript
- ✅ Modern API: Uses Google Maps JavaScript API v3 with AdvancedMarkerElement
- ✅ TypeScript: Full TypeScript support with type definitions
- ✅ 14 Languages: Built-in translations for English, Arabic, Spanish, French, German, and more
- ✅ RTL Support: Automatic right-to-left layout for Arabic, Hebrew, Farsi, and Urdu
- ✅ Geolocation: Built-in geolocation button with smart error handling
- ✅ Comprehensive Components: Map, Markers, Info Windows, Shapes, Clustering, Street View
- ✅ Accessible: ARIA labels and keyboard navigation support
- ✅ Zero Dependencies: Only Lit framework (~50KB)
Installation
npm install @salla.sa/ui-google-map-renderBundle Size: ~17KB gzipped (only Lit + component code)
Lightweight Design Philosophy
This library is designed to be extremely lightweight:
- ✅ Only core component logic is bundled (~67KB uncompressed, ~17KB gzipped)
- ✅ Google Maps libraries loaded dynamically at runtime from Google CDN
- ✅ Marker clustering is completely optional (not bundled)
- ✅ Zero dependencies except Lit framework
Optional: Marker Clustering Support
The <gmap-cluster> component requires the @googlemaps/markerclusterer package:
npm install @googlemaps/markerclustererThis is only needed if you use <gmap-cluster>. All other components work without any additional dependencies.
Interactive Builder
🎨 Try the interactive builder to customize your map and generate code!
Open builder.html in your browser for a visual interface that lets you:
- Adjust map properties in real-time
- Customize marker styling and colors
- Enable/disable features like geolocation and autocomplete
- See available events with live monitoring
- Generate ready-to-use code for Vanilla JS, Vue.js, or React
Simply open the file in your browser to start building!
Quick Start
1. Load the Google Maps API
import { load } from '@salla.sa/ui-google-map-render';
load({
key: 'YOUR_API_KEY',
version: 'quarterly',
libraries: ['marker', 'places'],
language: 'en',
region: 'US',
mapIds: ['YOUR_MAP_ID'], // Required for AdvancedMarkerElement
});2. Use the Components
<gmap-map
center='{"lat": 24.7136, "lng": 46.6753}'
zoom="12"
enable-geolocation
language="en"
>
<gmap-marker
position='{"lat": 24.7136, "lng": 46.6753}'
background="#EA4335"
title="My Location"
>
<gmap-info-window opened>
<div>
<h3>Hello World!</h3>
<p>This is an info window</p>
</div>
</gmap-info-window>
</gmap-marker>
</gmap-map>3. Custom Map Styling (Optional)
Customize your map appearance using cloud-based Map Styles in Google Cloud Console.
Steps:
- Go to Google Cloud Console → Map Styles
- Create a new style or import JSON
- Save and copy your Map ID
- Use the Map ID in your component
<gmap-map
center='{"lat": 24.7136, "lng": 46.6753}'
zoom="12"
map-id="YOUR_CUSTOM_STYLED_MAP_ID"
>
</gmap-map>📖 Guides:
- Map Styling Guide - Pre-made styles and custom styling
- Cloud-Based Styling - Complete styling documentation
Components
<gmap-map> - Main Map Component
The core map component that renders a Google Map.
Properties:
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| center | {lat, lng} | Required | Map center coordinates |
| zoom | number | 10 | Zoom level (0-22) |
| mapId | string | - | Google Cloud Map ID (required for markers, also controls styling) |
| mapTypeId | string | 'roadmap' | Map type: roadmap, hybrid, satellite, terrain |
| gestureHandling | string | 'cooperative' | Gesture handling mode |
| enableGeolocation | boolean | false | Show geolocation button |
| showLocationMarker | boolean | true | Show marker at user's location (when using geolocation) |
| locationMarkerPosition | {lat, lng, accuracy?} | - | Initial location marker position |
| locationMarkerBackground | string | '#4285F4' | Location marker background color |
| locationMarkerBorderColor | string | '#1967D2' | Location marker border color |
| locationMarkerGlyphColor | string | '#FFFFFF' | Location marker glyph color |
| locationMarkerScale | number | 1.2 | Location marker size scale |
| locationCircleOptions | object | - | Custom circle options (fillColor, strokeColor, etc.) |
| language | string | 'en' | UI language code |
| geolocationButtonLabel | string | - | Custom geolocation button text |
| geolocationErrorTitle | string | - | Custom error title text |
| geolocation* | string | - | See Translation Overrides |
| minZoom | number | 2 | Minimum zoom level |
| maxZoom | number | 21 | Maximum zoom level |
| mapTypeControl | boolean | false | Show/hide map type control (satellite/terrain switcher) |
| streetViewControl | boolean | false | Show/hide street view control |
Events:
Native Google Maps events (keep original names):
click,dblclick,drag,dragend,dragstart,idle,mousemove,mouseout,mouseover
Standardized custom events (kebab-case with gmap- prefix):
gmap-ready- Map is initialized and readygmap-center-changed- Map center has changedgmap-zoom-changed- Map zoom level has changedgmap-bounds-changed- Map bounds have changedgmap-geolocation-success- Geolocation successfulgmap-geolocation-error- Geolocation errorgmap-geolocation-loading- Geolocation in progressgmap-location-changed- Unified event for both initial location and marker drag (recommended)gmap-location-marker-dragend- User dragged the location marker (specific event)
Methods:
panBy(x, y)- Pan map by pixelspanTo(latLng)- Pan to coordinatesfitBounds(bounds)- Fit map to boundsresize()- Trigger map resizegetCurrentLocation()- Get user's location via browser geolocationsetLocation(lat, lng, accuracy?, panTo?)- Programmatically set location marker
UI Controls:
By default, the map has a clean minimal UI with only zoom controls visible. The map type control (satellite/terrain switcher) and street view controls are hidden by default for a cleaner interface.
To enable them:
<!-- Show map type control (satellite/terrain) -->
<gmap-map map-type-control></gmap-map>
<!-- Show street view control -->
<gmap-map street-view-control></gmap-map>
<!-- Show all controls -->
<gmap-map map-type-control street-view-control></gmap-map><gmap-marker> - Advanced Marker
Modern marker using Google Maps AdvancedMarkerElement API.
Important: Requires mapId to be set on the parent <gmap-map>.
Properties:
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| position | {lat, lng} | Required | Marker position |
| title | string | - | Hover tooltip |
| gmpDraggable | boolean | false | Enable dragging |
| gmpClickable | boolean | true | Enable clicking |
| background | string | - | Pin background color |
| borderColor | string | - | Pin border color |
| glyphColor | string | - | Pin icon color |
| scale | number | 1 | Pin size multiplier |
| icon | string | - | Custom icon URL (legacy) |
Events:
Native Google Maps events:
click,drag,dragstart,dragend
Custom events (with gmap- prefix):
gmap-marker-ready- Marker is initialized and readygmap-position-changed- Marker position has changed (e.g., after drag)
<gmap-info-window> - Info Window
Popup windows that display content on the map.
Properties:
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| opened | boolean | true | Window visibility |
| position | {lat, lng} | - | Position (for standalone windows) |
| zIndex | number | - | Stacking order |
Usage:
<!-- Attached to marker -->
<gmap-marker position='{"lat": 24.7136, "lng": 46.6753}'>
<gmap-info-window opened>
<div>Content here</div>
</gmap-info-window>
</gmap-marker>
<!-- Standalone -->
<gmap-info-window position='{"lat": 24.7136, "lng": 46.6753}' opened>
<div>Floating content</div>
</gmap-info-window>Shape Components
<gmap-polyline> - Connected Lines
| Property | Type | Description |
|----------|------|-------------|
| path | Array<{lat, lng}> | Array of coordinates |
| editable | boolean | Allow editing |
| draggable | boolean | Allow dragging |
| options | object | Polyline options (strokeColor, etc.) |
Events:
gmap-path-changed- Path has been modified
<gmap-polygon> - Closed Shape
| Property | Type | Description |
|----------|------|-------------|
| path | Array<{lat, lng}> | Single polygon path |
| paths | Array<Array<{lat, lng}>> | Multiple paths (with holes) |
| editable | boolean | Allow editing |
| draggable | boolean | Allow dragging |
| options | object | Polygon options (fillColor, etc.) |
Events:
gmap-path-changed- Path has been modified (single path)gmap-paths-changed- Paths have been modified (multiple paths with holes)
<gmap-circle> - Circular Overlay
| Property | Type | Description |
|----------|------|-------------|
| center | {lat, lng} | Circle center |
| radius | number | Radius in meters |
| editable | boolean | Allow resize/move |
| draggable | boolean | Allow dragging |
| options | object | Circle options |
<gmap-rectangle> - Rectangular Overlay
| Property | Type | Description |
|----------|------|-------------|
| bounds | {north, south, east, west} | Rectangle bounds |
| editable | boolean | Allow resize/move |
| draggable | boolean | Allow dragging |
| options | object | Rectangle options |
<gmap-cluster> - Marker Clustering
Groups nearby markers into clusters for better performance.
Requirements: Install @googlemaps/markerclusterer
<gmap-cluster grid-size="60">
<gmap-marker position='{"lat": 24.71, "lng": 46.67}'></gmap-marker>
<gmap-marker position='{"lat": 24.72, "lng": 46.68}'></gmap-marker>
<!-- More markers -->
</gmap-cluster><gmap-street-view-panorama> - Street View
Display Google Street View panorama.
| Property | Type | Description |
|----------|------|-------------|
| position | {lat, lng} | Panorama location |
| pano | string | Specific panorama ID |
| zoom | number | Zoom level (0-5) |
| pov | {heading, pitch} | Point of view |
Event Naming Convention
All custom events follow a standardized naming convention for consistency:
- Native Google Maps events: Keep their original names (
click,dblclick,drag, etc.) - Custom events: Use kebab-case with
gmap-prefix (gmap-ready,gmap-center-changed, etc.)
This ensures:
- ✅ Clear distinction between native and custom events
- ✅ No naming conflicts with other libraries
- ✅ Consistent, professional naming across all components
- ✅ Better compatibility with HTML attributes
// Native Google Maps events (no prefix)
map.addEventListener('click', (e) => { ... });
map.addEventListener('dragend', (e) => { ... });
// Custom events (gmap- prefix, kebab-case)
map.addEventListener('gmap-ready', (e) => { ... });
map.addEventListener('gmap-center-changed', (e) => { ... });
map.addEventListener('gmap-geolocation-success', (e) => { ... });Geolocation
The map component includes a built-in geolocation feature with smart error handling and a draggable marker.
Basic Usage
<!-- With marker (default) -->
<gmap-map
enable-geolocation
map-id="YOUR_MAP_ID"
language="ar"
geolocation-button-label="استخدم موقعي"
>
</gmap-map>
<!-- Without marker (just get coordinates) -->
<gmap-map
enable-geolocation
show-location-marker="false"
>
</gmap-map>Initial Location Marker
Set an initial location marker when the map loads:
<gmap-map
map-id="YOUR_MAP_ID"
location-marker-position='{"lat": 24.7136, "lng": 46.6753, "accuracy": 50}'
>
</gmap-map>Customize Marker Style
Customize the location marker colors and size:
<gmap-map
enable-geolocation
map-id="YOUR_MAP_ID"
location-marker-background="#EA4335"
location-marker-border-color="#C5221F"
location-marker-glyph-color="#FFFFFF"
location-marker-scale="1.5"
>
</gmap-map>Programmatic Location Setting
Set the location marker programmatically using JavaScript:
const map = document.querySelector('gmap-map');
// Wait for map to be ready
map.addEventListener('gmap-ready', () => {
// Set location marker (with pan)
map.setLocation(24.7136, 46.6753, 100);
// Set location without panning
map.setLocation(24.7136, 46.6753, 100, false);
});
// Update location dynamically (e.g., from a form or API)
function updateLocation(lat, lng) {
map.setLocation(lat, lng, 50);
}Features:
- 📍 Optional draggable marker - users can adjust their location by dragging
- 🎯 Accuracy circle that follows the marker
- 🔔 Unified
gmap-location-changedevent for simplified DX - ⚙️ Control marker visibility with
show-location-markerproperty - 🎨 Fully customizable marker colors and style
- 🚀 Programmatic location setting via
setLocation()method - 📌 Initial location support via
location-marker-positionproperty
Events:
// Recommended: Use the unified event for simplified DX
map.addEventListener('gmap-location-changed', (e) => {
const { position, accuracy, source } = e.detail;
console.log('Location:', position); // { lat, lng }
console.log('Accuracy:', accuracy); // meters
console.log('Source:', source); // 'geolocation', 'marker-drag', or 'programmatic'
// Handle all location changes with one event!
// Sources:
// - 'geolocation': Browser geolocation API
// - 'marker-drag': User dragged the marker
// - 'programmatic': Set via setLocation() method
// Perfect for updating forms or triggering geocoding
});
// Alternative: Listen to specific events
map.addEventListener('gmap-geolocation-success', (e) => {
console.log('Location:', e.detail.position);
console.log('Accuracy:', e.detail.accuracy);
});
map.addEventListener('gmap-geolocation-error', (e) => {
console.error('Error:', e.detail.code, e.detail.message);
});
map.addEventListener('gmap-geolocation-loading', (e) => {
console.log('Getting location...', e.detail.message);
});
// Specific event for marker drag (if you need to distinguish)
map.addEventListener('gmap-location-marker-dragend', (e) => {
console.log('Marker dragged to:', e.detail.position);
console.log('Accuracy:', e.detail.accuracy);
});Translations
Built-in support for 14 languages:
- English (en)
- Arabic (ar) - RTL
- Spanish (es)
- French (fr)
- German (de)
- Chinese (zh)
- Japanese (ja)
- Portuguese (pt)
- Russian (ru)
- Italian (it)
- Dutch (nl)
- Turkish (tr)
- Korean (ko)
- Hindi (hi)
<gmap-map language="ar"></gmap-map>Override translations:
<gmap-map
language="en"
geolocation-button-label="Find Me"
geolocation-loading-title="Finding your location..."
geolocation-error-title="Location Error"
geolocation-error-button-text="OK"
geolocation-click-to-select-message="You can also click on the map to select a location"
geolocation-permission-denied-message="Please allow location access in your browser settings"
geolocation-position-unavailable-message="Unable to determine your location"
geolocation-timeout-message="Location request timed out"
geolocation-general-error-message="An error occurred while getting your location"
>
</gmap-map>All Available Geolocation Override Attributes:
| Attribute | Purpose | Default (English) |
|-----------|---------|-------------------|
| geolocation-button-label | Button text | "" (icon only) |
| geolocation-loading-title | Loading overlay title | "Getting Your Location" |
| geolocation-loading-instructions | Loading overlay instructions | "Please allow location access..." |
| geolocation-error-title | Error overlay title | "Location Error" |
| geolocation-error-button-text | Error close button text | "Continue" |
| geolocation-error-timer-message | Error auto-hide message | "This message will auto-hide..." |
| geolocation-click-to-select-message | Hint in error overlay | "You can click on the map..." |
| geolocation-permission-denied-message | Permission denied error | "Location permission denied..." |
| geolocation-position-unavailable-message | Position unavailable error | "Your location is unavailable..." |
| geolocation-timeout-message | Timeout error | "Location request timed out..." |
| geolocation-general-error-message | General error fallback | "Unable to get your location..." |
Framework Integration
React
import '@salla.sa/ui-google-map-render';
import { load } from '@salla.sa/ui-google-map-render';
// Load API once
load({ key: 'YOUR_API_KEY', mapIds: ['YOUR_MAP_ID'] });
function MapComponent() {
const handleCenterChange = (e) => {
console.log('Center:', e.detail);
};
return (
<gmap-map
center={{ lat: 24.7136, lng: 46.6753 }}
zoom={12}
onCenter_changed={handleCenterChange}
>
<gmap-marker position={{ lat: 24.7136, lng: 46.6753 }} />
</gmap-map>
);
}Vue
<template>
<gmap-map
:center="center"
:zoom="12"
@center_changed="handleCenterChange"
>
<gmap-marker :position="center" />
</gmap-map>
</template>
<script setup>
import '@salla.sa/ui-google-map-render';
import { load } from '@salla.sa/ui-google-map-render';
import { ref } from 'vue';
load({ key: 'YOUR_API_KEY', mapIds: ['YOUR_MAP_ID'] });
const center = ref({ lat: 24.7136, lng: 46.6753 });
const handleCenterChange = (e) => {
console.log('Center:', e.detail);
};
</script>Angular
import '@salla.sa/ui-google-map-render';
import { load } from '@salla.sa/ui-google-map-render';
// In app module or component
load({ key: 'YOUR_API_KEY', mapIds: ['YOUR_MAP_ID'] });<gmap-map
[center]="center"
[zoom]="12"
(center_changed)="handleCenterChange($event)"
>
<gmap-marker [position]="center"></gmap-marker>
</gmap-map>Performance & Bundle Size
Runtime Library Loading
All Google Maps libraries are loaded dynamically at runtime:
// These are loaded from Google CDN when needed, NOT bundled:
// - marker library (AdvancedMarkerElement, PinElement)
// - places library (if you add autocomplete features)
// - Any other Google Maps libraries
// Example: Marker library is automatically loaded when first marker is createdWhat's in the Bundle?
- ✅ Component logic (~50KB)
- ✅ Lit framework (~50KB)
- ✅ Translation strings for 14 languages (~10KB)
- ✅ TypeScript types (0KB at runtime)
Total: ~17KB gzipped
What's NOT in the Bundle?
- ❌ Google Maps API (loaded from CDN)
- ❌ Marker library (loaded at runtime)
- ❌ Clustering library (optional peer dependency)
- ❌ Any Google Maps code
Best Practices
1. API Key Security
Restrict your API key by HTTP referrer in Google Cloud Console:
https://yourdomain.com/*2. Map ID Setup
Create a Map ID in Google Cloud Console for AdvancedMarkerElement: https://console.cloud.google.com/google/maps-apis/studio/maps
3. Version Pinning
Use quarterly version for production stability:
load({
key: 'YOUR_API_KEY',
version: 'quarterly', // Recommended for production
});4. Lazy Loading
Load the map API only when needed:
// Load API when user interacts
button.addEventListener('click', async () => {
await import('@salla.sa/ui-google-map-render');
load({ key: 'YOUR_API_KEY' });
});5. Performance
- Use clustering for >50 markers
- Limit the number of info windows open simultaneously
- Use appropriate zoom levels for your use case
TypeScript
Full TypeScript support included:
import type {
LoadOptions,
GeolocationResult,
GeolocationError,
} from '@salla.sa/ui-google-map-render';
const options: LoadOptions = {
key: 'YOUR_API_KEY',
version: 'quarterly',
libraries: ['marker'],
mapIds: ['YOUR_MAP_ID'],
};Browser Support
- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+
Requires ES2020 support and Web Components v1.
Documentation
- Performance & Bundle Size - Detailed analysis of how we keep the bundle lightweight
- Implementation Notes - Technical details and design decisions
- Full Component API - This document
License
MIT
Contributing
Contributions are welcome! Please open an issue or submit a pull request.
Support
For issues and questions:
- GitHub Issues: [Link to repo]
- Documentation: [Link to docs]
Why This Library?
- Smallest Bundle: At ~17KB gzipped, we're 50-75% smaller than alternatives
- Runtime Loading: Google Maps loaded from CDN, not bundled
- Modern Standards: Built with Web Components, works everywhere
- Zero Lock-in: Pure web components, use with any framework
- Production Ready: Based on battle-tested vue2-google-maps design
Acknowledgments
Built with Lit - Simple. Fast. Web Components.
Based on the feature set of vue2-google-maps but completely rewritten for modern web standards with a focus on bundle size and performance.
