Skip to main content

HTML Viewer Examples

Interactive HTML examples demonstrating esri-gl with different mapping libraries and service types. Click the links below to view live examples in your browser.

Live Examples​

🚀 Basic MapLibre GL Viewer​

A complete interactive viewer with:

  • Service type selector (Dynamic, Feature, Tiled, Vector)
  • Custom URL input with working examples
  • Click-to-identify functionality
  • Service loading/clearing controls
  • Professional styling with control panels

⚡ Minimal Example​

A bare-bones setup showing:

  • Simple esri-gl integration
  • Just 30 lines of code
  • Perfect for getting started quickly

📊 Dashboard Example​

An advanced dashboard-style interface featuring:

  • Professional sidebar with service buttons
  • Multiple predefined ArcGIS services
  • Service management and layer controls
  • Loading states and info panels
  • Interactive identify functionality

Code Examples​

Below are the complete source code examples you can copy and modify for your own projects.

Try the Live Examples First

Check out the live examples above to see esri-gl in action before diving into the code!

Basic MapLibre GL Setup​

A complete HTML page using esri-gl with MapLibre GL JS:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>esri-gl with MapLibre GL</title>

<!-- MapLibre GL CSS -->
<link href="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.css" rel="stylesheet">

<style>
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}

#map {
width: 100vw;
height: 100vh;
}

.controls {
position: absolute;
top: 10px;
right: 10px;
background: rgba(255, 255, 255, 0.9);
padding: 15px;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
z-index: 1000;
}

.control-group {
margin-bottom: 10px;
}

label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}

input, select, button {
width: 100%;
padding: 5px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 3px;
}

button {
background: #007cbf;
color: white;
border: none;
cursor: pointer;
font-weight: bold;
}

button:hover {
background: #005a87;
}

.info-popup {
background: white;
padding: 10px;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
max-width: 300px;
}

.info-popup h3 {
margin: 0 0 10px 0;
color: #007cbf;
}

.info-popup p {
margin: 5px 0;
font-size: 14px;
}
</style>
</head>
<body>
<div id="map"></div>

<div class="controls">
<div class="control-group">
<label>Service Type:</label>
<select id="serviceType">
<option value="dynamic">Dynamic Map Service</option>
<option value="feature">Feature Service</option>
<option value="tiled">Tiled Map Service</option>
<option value="vector">Vector Tile Service</option>
</select>
</div>

<div class="control-group">
<label>Service URL:</label>
<input type="text" id="serviceUrl"
value="https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer"
placeholder="Enter ArcGIS service URL">
</div>

<div class="control-group">
<button onclick="loadService()">Load Service</button>
<button onclick="clearMap()">Clear Map</button>
</div>

<div class="control-group">
<label>
<input type="checkbox" id="enableIdentify" onchange="toggleIdentify()">
Enable Click to Identify
</label>
</div>
</div>

<!-- MapLibre GL JavaScript -->
<script src="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js"></script>

<!-- esri-gl Library -->
<script src="https://unpkg.com/esri-gl@latest/dist/index.umd.js"></script>

<script>
// Initialize MapLibre GL map
const map = new maplibregl.Map({
container: 'map',
style: {
version: 8,
sources: {
'raster-tiles': {
type: 'raster',
tiles: [
'https://tile.openstreetmap.org/{z}/{x}/{y}.png'
],
tileSize: 256,
attribution: '© OpenStreetMap contributors'
}
},
layers: [
{
id: 'background',
type: 'raster',
source: 'raster-tiles'
}
]
},
center: [-95, 40],
zoom: 4
});

// The UMD bundle exposes everything under the global `esrigl`
const { DynamicMapService, FeatureService, TiledMapService, VectorTileService } = esrigl;

// Global variables
let currentService = null;
let identifyEnabled = false;
let currentPopup = null;

// Wait for map to load
map.on('load', () => {
console.log('Map loaded successfully');
});

// Load service based on type
function loadService() {
const serviceType = document.getElementById('serviceType').value;
const serviceUrl = document.getElementById('serviceUrl').value.trim();

if (!serviceUrl) {
alert('Please enter a service URL');
return;
}

// Clear existing service
clearMap();

try {
const sourceId = 'esri-service-source';

switch (serviceType) {
case 'dynamic':
currentService = new DynamicMapService(sourceId, map, {
url: serviceUrl
});

// Add raster layer
map.addLayer({
id: 'dynamic-layer',
type: 'raster',
source: sourceId
});

console.log('Dynamic Map Service loaded');
break;

case 'feature':
currentService = new FeatureService(sourceId, map, {
url: serviceUrl
});

// Add vector layers after service is ready
setTimeout(() => {
addFeatureServiceLayers(sourceId);
}, 1000);

console.log('Feature Service loaded');
break;

case 'tiled':
currentService = new TiledMapService(sourceId, map, {
url: serviceUrl
});

// Add raster layer
map.addLayer({
id: 'tiled-layer',
type: 'raster',
source: sourceId
});

console.log('Tiled Map Service loaded');
break;

case 'vector':
currentService = new VectorTileService(sourceId, map, {
url: serviceUrl
});

console.log('Vector Tile Service loaded');
break;

default:
alert('Unknown service type');
return;
}

// Update URL examples based on service type
updateUrlExample(serviceType);

} catch (error) {
console.error('Error loading service:', error);
alert('Error loading service. Please check the URL and try again.');
}
}

// Add layers for Feature Service
function addFeatureServiceLayers(sourceId) {
try {
// Get the source to check if it's loaded
const source = map.getSource(sourceId);
if (!source) {
console.warn('Source not ready yet');
return;
}

// Add fill layer for polygons
if (!map.getLayer('features-fill')) {
map.addLayer({
id: 'features-fill',
type: 'fill',
source: sourceId,
paint: {
'fill-color': '#088',
'fill-opacity': 0.6
},
filter: ['==', '$type', 'Polygon']
});
}

// Add line layer
if (!map.getLayer('features-line')) {
map.addLayer({
id: 'features-line',
type: 'line',
source: sourceId,
paint: {
'line-color': '#000',
'line-width': 2
},
filter: ['any', ['==', '$type', 'LineString'], ['==', '$type', 'Polygon']]
});
}

// Add circle layer for points
if (!map.getLayer('features-circle')) {
map.addLayer({
id: 'features-circle',
type: 'circle',
source: sourceId,
paint: {
'circle-color': '#ff0000',
'circle-radius': 6,
'circle-stroke-width': 2,
'circle-stroke-color': '#ffffff'
},
filter: ['==', '$type', 'Point']
});
}

} catch (error) {
console.error('Error adding feature layers:', error);
}
}

// Clear map
function clearMap() {
if (currentService && typeof currentService.remove === 'function') {
currentService.remove();
}

// Remove layers
const layersToRemove = ['dynamic-layer', 'tiled-layer', 'features-fill', 'features-line', 'features-circle'];
layersToRemove.forEach(layerId => {
if (map.getLayer(layerId)) {
map.removeLayer(layerId);
}
});

// Remove sources
const sourcesToRemove = ['esri-service-source'];
sourcesToRemove.forEach(sourceId => {
if (map.getSource(sourceId)) {
map.removeSource(sourceId);
}
});

currentService = null;

// Close popup
if (currentPopup) {
currentPopup.remove();
currentPopup = null;
}

console.log('Map cleared');
}

// Toggle identify functionality
function toggleIdentify() {
identifyEnabled = document.getElementById('enableIdentify').checked;

if (identifyEnabled) {
map.on('click', handleMapClick);
map.getCanvas().style.cursor = 'crosshair';
} else {
map.off('click', handleMapClick);
map.getCanvas().style.cursor = '';
if (currentPopup) {
currentPopup.remove();
currentPopup = null;
}
}
}

// Handle map click for identify
async function handleMapClick(e) {
if (!identifyEnabled || !currentService) return;

try {
const serviceType = document.getElementById('serviceType').value;

if (serviceType === 'dynamic' && currentService.identify) {
// Use service identify method (raw ArcGIS response)
const response = await currentService.identify(e.lngLat);
showIdentifyResults(e.lngLat, response);

} else if (serviceType === 'feature') {
// Query features at click point
const features = map.queryRenderedFeatures(e.point, {
layers: ['features-fill', 'features-line', 'features-circle']
});

if (features.length > 0) {
showFeatureInfo(e.lngLat, features[0]);
} else {
showNoResults(e.lngLat);
}

} else {
showMessage(e.lngLat, 'Identify not supported for this service type');
}

} catch (error) {
console.error('Identify error:', error);
showMessage(e.lngLat, 'Error identifying features');
}
}

// Show identify results — DynamicMapService.identify() resolves to the raw
// ArcGIS response, so results live on `response.results`, not `features`.
function showIdentifyResults(lngLat, response) {
if (currentPopup) {
currentPopup.remove();
}

if (!response || !response.results || response.results.length === 0) {
showNoResults(lngLat);
return;
}

const props = response.results[0].attributes || {};

let content = '<div class="info-popup"><h3>Feature Information</h3>';

Object.keys(props).slice(0, 5).forEach(key => {
if (props[key] !== null && props[key] !== undefined) {
content += `<p><strong>${key}:</strong> ${props[key]}</p>`;
}
});

content += '</div>';

currentPopup = new maplibregl.Popup()
.setLngLat(lngLat)
.setHTML(content)
.addTo(map);
}

// Show feature information
function showFeatureInfo(lngLat, feature) {
if (currentPopup) {
currentPopup.remove();
}

const props = feature.properties || {};

let content = '<div class="info-popup"><h3>Feature Properties</h3>';

Object.keys(props).slice(0, 8).forEach(key => {
if (props[key] !== null && props[key] !== undefined) {
content += `<p><strong>${key}:</strong> ${props[key]}</p>`;
}
});

content += '</div>';

currentPopup = new maplibregl.Popup()
.setLngLat(lngLat)
.setHTML(content)
.addTo(map);
}

// Show no results message
function showNoResults(lngLat) {
showMessage(lngLat, 'No features found at this location');
}

// Show message popup
function showMessage(lngLat, message) {
if (currentPopup) {
currentPopup.remove();
}

currentPopup = new maplibregl.Popup()
.setLngLat(lngLat)
.setHTML(`<div class="info-popup"><p>${message}</p></div>`)
.addTo(map);
}

// Update URL example based on service type
function updateUrlExample(serviceType) {
const urlInput = document.getElementById('serviceUrl');

const examples = {
dynamic: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer',
feature: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer/0',
tiled: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/WorldTimeZones/MapServer',
vector: 'https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer'
};

if (examples[serviceType] && urlInput.value.includes('sampleserver6')) {
urlInput.value = examples[serviceType];
}
}

// Initialize service type change handler
document.getElementById('serviceType').addEventListener('change', (e) => {
updateUrlExample(e.target.value);
});

// Add keyboard shortcuts
document.addEventListener('keydown', (e) => {
if (e.key === 'c' && e.ctrlKey) {
clearMap();
e.preventDefault();
}
if (e.key === 'l' && e.ctrlKey) {
loadService();
e.preventDefault();
}
});

console.log('esri-gl HTML viewer loaded successfully');
console.log('Available services:', Object.keys(esrigl));
</script>
</body>
</html>

Mapbox GL JS Version​

A complete HTML page using esri-gl with Mapbox GL JS:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>esri-gl with Mapbox GL JS</title>

<!-- Mapbox GL CSS -->
<link href="https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.css" rel="stylesheet">

<style>
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}

#map {
width: 100vw;
height: 100vh;
}

.attribution {
position: absolute;
bottom: 0;
right: 0;
background: rgba(255, 255, 255, 0.8);
padding: 2px 5px;
font-size: 12px;
z-index: 1000;
}
</style>
</head>
<body>
<div id="map"></div>
<div class="attribution">
Powered by <a href="https://github.com/muimsd/esri-gl" target="_blank">esri-gl</a>
</div>

<!-- Mapbox GL JavaScript -->
<script src="https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.js"></script>

<!-- esri-gl Library -->
<script src="https://unpkg.com/esri-gl@latest/dist/index.umd.js"></script>

<script>
// Set your Mapbox access token (required for Mapbox GL JS)
mapboxgl.accessToken = 'your-mapbox-access-token';

// Initialize Mapbox GL map
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v11',
center: [-95, 40],
zoom: 4
});

// Add navigation control
map.addControl(new mapboxgl.NavigationControl());

// The UMD bundle exposes everything under the global `esrigl`
const { DynamicMapService, FeatureService } = esrigl;

map.on('load', () => {
// Add Dynamic Map Service
const dynamicService = new DynamicMapService('usa-service', map, {
url: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer'
});

// Add the layer to map
map.addLayer({
id: 'usa-layer',
type: 'raster',
source: 'usa-service'
});

// Add Feature Service
const featureService = new FeatureService('cities-service', map, {
url: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer/0'
});

// Add feature layers
setTimeout(() => {
map.addLayer({
id: 'cities-points',
type: 'circle',
source: 'cities-service',
paint: {
'circle-radius': 8,
'circle-color': '#ff6b35',
'circle-stroke-width': 2,
'circle-stroke-color': '#ffffff'
}
});
}, 2000);

console.log('Mapbox GL map with esri-gl services loaded');
});
</script>
</body>
</html>

Minimal Example​

A minimal HTML example for quick testing:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minimal esri-gl Example</title>
<link href="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.css" rel="stylesheet">
<style>
body { margin: 0; padding: 0; }
#map { width: 100vw; height: 100vh; }
</style>
</head>
<body>
<div id="map"></div>

<script src="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js"></script>
<script src="https://unpkg.com/esri-gl@latest/dist/index.umd.js"></script>

<script>
const map = new maplibregl.Map({
container: 'map',
style: 'https://demotiles.maplibre.org/style.json',
center: [-95, 40],
zoom: 4
});

map.on('load', () => {
// Add ArcGIS Dynamic Map Service (UMD global is `esrigl`)
new esrigl.DynamicMapService('esri-service', map, {
url: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer'
});

// Add the layer
map.addLayer({
id: 'esri-layer',
type: 'raster',
source: 'esri-service'
});
});
</script>
</body>
</html>

Interactive Dashboard Example​

A more advanced dashboard-style HTML example:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>esri-gl Dashboard</title>
<link href="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.css" rel="stylesheet">

<style>
* { box-sizing: border-box; }
body { margin: 0; padding: 0; font-family: 'Segoe UI', Arial, sans-serif; }

.container {
display: flex;
height: 100vh;
}

.sidebar {
width: 300px;
background: #2c3e50;
color: white;
padding: 20px;
overflow-y: auto;
}

.sidebar h2 {
margin-top: 0;
color: #3498db;
}

.service-group {
margin-bottom: 25px;
padding: 15px;
background: rgba(52, 152, 219, 0.1);
border-radius: 5px;
}

.service-group h3 {
margin: 0 0 15px 0;
color: #ecf0f1;
}

.service-item {
margin-bottom: 10px;
}

.service-item button {
width: 100%;
padding: 8px 12px;
background: #3498db;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 14px;
}

.service-item button:hover {
background: #2980b9;
}

.service-item button.active {
background: #e74c3c;
}

#map {
flex: 1;
position: relative;
}

.map-info {
position: absolute;
top: 10px;
left: 10px;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px;
border-radius: 5px;
font-size: 14px;
z-index: 1000;
}

.loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 20px;
border-radius: 5px;
z-index: 2000;
display: none;
}
</style>
</head>
<body>
<div class="container">
<div class="sidebar">
<h2>esri-gl Dashboard</h2>

<div class="service-group">
<h3>Map Services</h3>
<div class="service-item">
<button onclick="loadService('dynamic', 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer', 'USA Map')">USA Dynamic Service</button>
</div>
<div class="service-item">
<button onclick="loadService('tiled', 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/WorldTimeZones/MapServer', 'World Time Zones')">World Time Zones</button>
</div>
<div class="service-item">
<button onclick="loadService('dynamic', 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer', 'Census Data')">Census Data</button>
</div>
</div>

<div class="service-group">
<h3>Feature Services</h3>
<div class="service-item">
<button onclick="loadService('feature', 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer/0', 'USA Cities')">USA Cities</button>
</div>
<div class="service-item">
<button onclick="loadService('feature', 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer/2', 'USA States')">USA States</button>
</div>
</div>

<div class="service-group">
<h3>Vector Services</h3>
<div class="service-item">
<button onclick="loadService('vector', 'https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer', 'World Basemap')">World Basemap</button>
</div>
</div>

<div class="service-group">
<h3>Actions</h3>
<div class="service-item">
<button onclick="clearAllServices()">Clear All</button>
</div>
<div class="service-item">
<button onclick="toggleIdentify()">Toggle Identify</button>
</div>
</div>
</div>

<div id="map">
<div class="loading" id="loading">Loading service...</div>
<div class="map-info" id="mapInfo">Click a service to load</div>
</div>
</div>

<script src="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js"></script>
<script src="https://unpkg.com/esri-gl@latest/dist/index.umd.js"></script>

<script>
const map = new maplibregl.Map({
container: 'map',
style: 'https://demotiles.maplibre.org/style.json',
center: [-95, 40],
zoom: 4
});

// Add controls
map.addControl(new maplibregl.NavigationControl());
map.addControl(new maplibregl.ScaleControl());

// The UMD bundle exposes everything under the global `esrigl`
const { DynamicMapService, FeatureService, TiledMapService, VectorTileService } = esrigl;

let services = new Map();
let layerCounter = 0;
let identifyEnabled = false;

function showLoading() {
document.getElementById('loading').style.display = 'block';
}

function hideLoading() {
document.getElementById('loading').style.display = 'none';
}

function updateMapInfo(text) {
document.getElementById('mapInfo').textContent = text;
}

async function loadService(type, url, name) {
showLoading();
updateMapInfo(`Loading ${name}...`);

try {
const serviceId = `service-${layerCounter++}`;
const layerId = `layer-${layerCounter}`;

let service;
switch (type) {
case 'dynamic':
service = new DynamicMapService(serviceId, map, { url });
map.addLayer({
id: layerId,
type: 'raster',
source: serviceId
});
break;

case 'tiled':
service = new TiledMapService(serviceId, map, { url });
map.addLayer({
id: layerId,
type: 'raster',
source: serviceId
});
break;

case 'feature':
service = new FeatureService(serviceId, map, {
url
});

setTimeout(() => {
// Add feature layers based on geometry
const source = map.getSource(serviceId);
if (source) {
map.addLayer({
id: `${layerId}-fill`,
type: 'fill',
source: serviceId,
paint: {
'fill-color': '#3498db',
'fill-opacity': 0.6
},
filter: ['==', '$type', 'Polygon']
});

map.addLayer({
id: `${layerId}-line`,
type: 'line',
source: serviceId,
paint: {
'line-color': '#2c3e50',
'line-width': 2
}
});

map.addLayer({
id: `${layerId}-circle`,
type: 'circle',
source: serviceId,
paint: {
'circle-radius': 6,
'circle-color': '#e74c3c',
'circle-stroke-width': 2,
'circle-stroke-color': '#ffffff'
},
filter: ['==', '$type', 'Point']
});
}
}, 1000);
break;

case 'vector':
service = new VectorTileService(serviceId, map, { url });
break;
}

services.set(serviceId, { service, name, type, layerId });
updateMapInfo(`Loaded: ${name} (${services.size} services active)`);

} catch (error) {
console.error('Error loading service:', error);
updateMapInfo(`Error loading ${name}`);
}

hideLoading();
}

function clearAllServices() {
services.forEach((serviceInfo, serviceId) => {
if (serviceInfo.service && typeof serviceInfo.service.remove === 'function') {
serviceInfo.service.remove();
}

// Remove layers
const layersToRemove = [
serviceInfo.layerId,
`${serviceInfo.layerId}-fill`,
`${serviceInfo.layerId}-line`,
`${serviceInfo.layerId}-circle`
];

layersToRemove.forEach(layerId => {
if (map.getLayer(layerId)) {
map.removeLayer(layerId);
}
});

// Remove source
if (map.getSource(serviceId)) {
map.removeSource(serviceId);
}
});

services.clear();
updateMapInfo('All services cleared');
}

function toggleIdentify() {
identifyEnabled = !identifyEnabled;

if (identifyEnabled) {
map.on('click', handleMapClick);
map.getCanvas().style.cursor = 'crosshair';
updateMapInfo('Identify mode enabled - click on features');
} else {
map.off('click', handleMapClick);
map.getCanvas().style.cursor = '';
updateMapInfo(`${services.size} services active`);
}
}

function handleMapClick(e) {
if (!identifyEnabled) return;

// Query all rendered features at click point
const features = map.queryRenderedFeatures(e.point);

if (features.length > 0) {
const feature = features[0];
const props = feature.properties || {};

let content = '<h3>Feature Properties</h3>';
Object.keys(props).slice(0, 5).forEach(key => {
if (props[key] !== null && props[key] !== undefined) {
content += `<p><strong>${key}:</strong> ${props[key]}</p>`;
}
});

new maplibregl.Popup()
.setLngLat(e.lngLat)
.setHTML(content)
.addTo(map);
}
}

map.on('load', () => {
updateMapInfo('Map ready - click a service to load');
});
</script>
</body>
</html>

Usage Instructions​

All examples use CDN links for easy setup:

  • MapLibre GL: https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js
  • Mapbox GL: https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.js
  • esri-gl: https://unpkg.com/esri-gl@latest/dist/index.umd.js

Local Development​

For local development, you can:

  1. Download the files and reference local paths
  2. Use npm/yarn to install packages and bundle
  3. Build esri-gl locally and reference the dist files

Key Features Demonstrated​

  • Multiple Service Types: Dynamic Map, Feature, Tiled Map, and Vector Tile services
  • Interactive Controls: Service switching, layer management, identify functionality
  • Responsive Design: Mobile-friendly layouts
  • Error Handling: Graceful handling of service loading errors
  • Performance: Efficient layer management and cleanup

Save any of these HTML files and open them in a web browser to see esri-gl in action!