Create 2D and 3D maps using ArcGIS Maps SDK for JavaScript. Use for initializing maps, scenes, views, and navigation. Supports both Map Components (web components) and Core API approaches.
76
71%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advisory
Suggest reviewing before use
Optimize this skill with Tessl
npx tessl skill review --optimize ./contexts/5.0/skills/arcgis-core-maps/SKILL.mdUse this skill when creating 2D maps (MapView) or 3D scenes (SceneView) with the ArcGIS Maps SDK for JavaScript.
Use with Vite, webpack, Rollup, or other build tools:
import Map from "@arcgis/core/Map.js";
import MapView from "@arcgis/core/views/MapView.js";
import FeatureLayer from "@arcgis/core/layers/FeatureLayer.js";Use with CDN script tags when no build step is available:
const Map = await $arcgis.import("@arcgis/core/Map.js");
const MapView = await $arcgis.import("@arcgis/core/views/MapView.js");
// Multiple imports
const [FeatureLayer, Graphic] = await $arcgis.import([
"@arcgis/core/layers/FeatureLayer.js",
"@arcgis/core/Graphic.js"
]);<script src="https://js.arcgis.com/5.0/"></script> in HTMLNote: The examples in this skill use Direct ESM imports. For CDN usage, replace
import X from "path"withconst X = await $arcgis.import("path").
The ArcGIS SDK supports autocasting - passing plain objects instead of class instances.
Use new SimpleRenderer(), new Point(), etc. when:
instanceof checksnew SimpleRenderer({ ... }) exposes properties clearlyimport SimpleRenderer from "@arcgis/core/renderers/SimpleRenderer.js";
import SimpleMarkerSymbol from "@arcgis/core/symbols/SimpleMarkerSymbol.js";
const renderer = new SimpleRenderer({
symbol: new SimpleMarkerSymbol({
color: [226, 119, 40],
size: 8
})
});Use plain objects with type property when:
layer.renderer = { ... } works cleanly in React useEffect// Use 'as const' or 'satisfies' to keep discriminated unions narrow
const renderer = {
type: "simple",
symbol: {
type: "simple-marker",
color: [226, 119, 40],
size: 8
}
} as const;
// Or with satisfies for better type inference
const renderer = {
type: "simple",
symbol: {
type: "simple-marker",
color: [226, 119, 40],
size: 8
}
} satisfies __esri.SimpleRendererProperties;The real TypeScript concern is keeping discriminated unions narrow:
// ❌ BAD - type widens to string
const symbol = { type: "simple-marker", color: "red" };
// ✅ GOOD - type stays literal
const symbol = { type: "simple-marker", color: "red" } as const;
// ✅ GOOD - explicit type annotation
const symbol: __esri.SimpleMarkerSymbolProperties = {
type: "simple-marker",
color: "red"
};as const or satisfies to maintain type safety with autocastingWeb components approach using <arcgis-map> and <arcgis-scene>.
Traditional JavaScript approach using Map, MapView, and SceneView classes.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>ArcGIS Map</title>
<style>
html, body { height: 100%; margin: 0; }
</style>
<!-- Load Calcite components -->
<script type="module" src="https://js.arcgis.com/calcite-components/3.3.3/calcite.esm.js"></script>
<!-- Load ArcGIS Maps SDK -->
<script src="https://js.arcgis.com/5.0/"></script>
<!-- Load Map components -->
<script type="module" src="https://js.arcgis.com/5.0/map-components/"></script>
</head>
<body>
<arcgis-map basemap="topo-vector" center="-118.24,34.05" zoom="12">
<arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-map>
</body>
</html><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>ArcGIS Map</title>
<style>
html, body, #viewDiv { height: 100%; margin: 0; }
</style>
<!-- REQUIRED: main.css for Core API -->
<link rel="stylesheet" href="https://js.arcgis.com/5.0/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/5.0/"></script>
</head>
<body>
<div id="viewDiv"></div>
<script type="module">
import Map from "@arcgis/core/Map.js";
import MapView from "@arcgis/core/views/MapView.js";
const map = new Map({ basemap: "topo-vector" });
const view = new MapView({
container: "viewDiv",
map: map,
center: [-118.24, 34.05], // [longitude, latitude]
zoom: 12
});
</script>
</body>
</html><arcgis-map basemap="topo-vector" center="-118.24,34.05" zoom="12">
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-compass slot="top-left"></arcgis-compass>
<arcgis-home slot="top-left"></arcgis-home>
<arcgis-locate slot="top-left"></arcgis-locate>
</arcgis-map>
<script type="module">
const mapElement = document.querySelector("arcgis-map");
await mapElement.viewOnReady(); // Wait for view to be ready
const view = mapElement.view; // Access the MapView
const map = mapElement.map; // Access the Map
</script>import Map from "@arcgis/core/Map.js";
import MapView from "@arcgis/core/views/MapView.js";
const map = new Map({ basemap: "streets-vector" });
const view = new MapView({
container: "viewDiv",
map: map,
center: [-118.24, 34.05],
zoom: 12,
// Optional constraints
constraints: {
minZoom: 5,
maxZoom: 18,
rotationEnabled: false
}
});<arcgis-scene basemap="topo-3d" ground="world-elevation">
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle>
</arcgis-scene>
<script type="module">
const sceneElement = document.querySelector("arcgis-scene");
await sceneElement.viewOnReady();
const view = sceneElement.view; // SceneView
</script>import Map from "@arcgis/core/Map.js";
import SceneView from "@arcgis/core/views/SceneView.js";
const map = new Map({
basemap: "topo-3d",
ground: "world-elevation"
});
const view = new SceneView({
container: "viewDiv",
map: map,
camera: {
position: {
longitude: -118.24,
latitude: 34.05,
z: 25000 // altitude in meters
},
heading: 0, // compass direction
tilt: 45 // 0 = straight down, 90 = horizon
}
});The arcgis-video component is a third view container alongside arcgis-map and arcgis-scene, used for displaying video feeds from video services.
<arcgis-video>
<arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-video>import "@arcgis/map-components/dist/components/arcgis-video";
const videoElement = document.querySelector("arcgis-video");
videoElement?.addEventListener("arcgisViewReadyChange", (event) => {
const { view } = event.detail;
console.log("Video view ready:", view);
});Note:
arcgis-videosupports the same position slots asarcgis-mapandarcgis-scene:top-left,top-right,bottom-left,bottom-right,top-start,top-end,bottom-start,bottom-end.
<!-- Map Components -->
<arcgis-map item-id="f2e9b762544945f390ca4ac3671cfa72">
<arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-map>// Core API
import MapView from "@arcgis/core/views/MapView.js";
import WebMap from "@arcgis/core/WebMap.js";
const webmap = new WebMap({
portalItem: { id: "f2e9b762544945f390ca4ac3671cfa72" }
});
const view = new MapView({
map: webmap,
container: "viewDiv"
});<!-- Map Components -->
<arcgis-scene item-id="YOUR_WEBSCENE_ID">
<arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-scene>// Core API
import SceneView from "@arcgis/core/views/SceneView.js";
import WebScene from "@arcgis/core/WebScene.js";
const webscene = new WebScene({
portalItem: { id: "YOUR_WEBSCENE_ID" }
});
const view = new SceneView({
map: webscene,
container: "viewDiv"
});| Component | Purpose |
|---|---|
arcgis-zoom | Zoom in/out buttons |
arcgis-compass | Orientation indicator, click to reset north |
arcgis-home | Return to initial extent |
arcgis-locate | Find user's location |
arcgis-navigation-toggle | Switch between pan/rotate modes (3D) |
arcgis-fullscreen | Toggle fullscreen mode |
arcgis-scale-bar | Display map scale |
<arcgis-map basemap="streets-vector">
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-home slot="top-left"></arcgis-home>
<arcgis-search slot="top-right"></arcgis-search>
<arcgis-legend slot="bottom-left"></arcgis-legend>
<arcgis-scale-bar slot="bottom-right"></arcgis-scale-bar>
</arcgis-map>Available slots: top-left, top-right, bottom-left, bottom-right, top-start, top-end, bottom-start, bottom-end
| Property | Type | Description |
|---|---|---|
basemap | string or Basemap | Base layer(s) for the map |
ground | string or Ground | Surface properties (elevation) |
layers | Collection | Operational layers |
tables | Collection | Non-spatial tables |
allLayers | Collection (readonly) | Flattened layer collection |
allTables | Collection (readonly) | Flattened table collection |
editableLayers | Collection (readonly) | Layers that support editing |
focusAreas | Collection (readonly) | Focus areas for the map |
// By center and zoom
const view = new MapView({
container: "viewDiv",
map: map,
center: [-118.24, 34.05],
zoom: 12
});
// By scale
const view = new MapView({
container: "viewDiv",
map: map,
center: [-118.24, 34.05],
scale: 50000 // 1:50,000
});
// By extent
const view = new MapView({
container: "viewDiv",
map: map,
extent: {
xmin: -118.5,
ymin: 33.8,
xmax: -117.9,
ymax: 34.3,
spatialReference: { wkid: 4326 }
}
});// Go to location
await view.goTo({
center: [-118.24, 34.05],
zoom: 15
});
// Animated navigation
await view.goTo(
{ center: [-118.24, 34.05], zoom: 15 },
{ duration: 2000, easing: "ease-in-out" }
);
// Go to extent
await view.goTo(layer.fullExtent);
// Go to features
await view.goTo(featureSet.features);// Constrain zoom levels
view.constraints = {
minZoom: 5,
maxZoom: 18
};
// Constrain to area
view.constraints = {
geometry: layer.fullExtent,
minScale: 500000
};
// Disable rotation
view.constraints = {
rotationEnabled: false
};| Property | Type | Description |
|---|---|---|
ready | boolean | Whether the view is fully initialized |
updating | boolean | Whether any layer or the view is updating |
stationary | boolean | True when no navigation is in progress |
interacting | boolean | True when user is interacting (pan/zoom) |
suspended | boolean | True when the view is not visible |
focused | boolean | True when the view has keyboard focus |
resolution | number | Map units per pixel |
// View ready
view.when(() => {
console.log("View is ready");
});
// Click event
view.on("click", (event) => {
console.log("Clicked at:", event.mapPoint);
});
// Pointer move
view.on("pointer-move", (event) => {
const point = view.toMap(event);
console.log("Mouse at:", point.longitude, point.latitude);
});
// Hit test (identify features under cursor)
view.on("click", async (event) => {
const response = await view.hitTest(event);
if (response.results.length > 0) {
const graphic = response.results[0].graphic;
console.log("Clicked feature:", graphic.attributes);
}
});
// Extent change (using reactiveUtils)
import * as reactiveUtils from "@arcgis/core/core/reactiveUtils.js";
reactiveUtils.watch(
() => view.extent,
(extent) => console.log("Extent changed:", extent)
);
// Stationary (after pan/zoom completes)
reactiveUtils.watch(
() => view.stationary,
(isStationary) => {
if (isStationary) {
console.log("Navigation complete");
}
}
);| Event | Description |
|---|---|
click | User clicks on the view |
double-click | User double-clicks on the view |
immediate-click | Fires immediately on click (no double-click delay) |
pointer-move | Mouse/touch pointer moves over the view |
pointer-down / pointer-up | Pointer press/release |
pointer-enter / pointer-leave | Pointer enters/leaves the view |
key-down / key-up | Keyboard events when view is focused |
drag | User drags (pan) on the view |
hold | User holds pointer on the view |
mouse-wheel | Mouse wheel scroll |
resize | View container resizes |
focus / blur | View gains/loses keyboard focus |
layerview-create | A layer view is created |
layerview-destroy | A layer view is destroyed |
| Basemap ID | Description |
|---|---|
streets-vector | Street map |
topo-vector | Topographic |
satellite | Satellite imagery |
hybrid | Satellite with labels |
dark-gray-vector | Dark gray canvas |
gray-vector | Light gray canvas |
osm | OpenStreetMap |
topo-3d | 3D topographic (SceneView) |
import esriRequest from "@arcgis/core/request.js";
// GET request with JSON response
const response = await esriRequest(url, {
query: { f: "json" },
responseType: "json"
});
console.log("Status:", response.httpStatus);
console.log("Data:", response.data);const response = await esriRequest(url, {
query: {
f: "json",
param1: "value1"
},
responseType: "json", // "json", "text", "array-buffer", "blob", "image"
method: "post", // "auto", "head", "post"
body: formData, // For POST requests
timeout: 30000, // Timeout in ms
headers: {
"X-Custom-Header": "value"
}
});// Image response
const imageResponse = await esriRequest(imageUrl, {
responseType: "image"
});
const imageElement = imageResponse.data;
// Binary data
const binaryResponse = await esriRequest(fileUrl, {
responseType: "array-buffer"
});
const arrayBuffer = binaryResponse.data;<arcgis-scene>
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle>
</arcgis-scene>
<script type="module">
import ElevationLayer from "@arcgis/core/layers/ElevationLayer.js";
import TileLayer from "@arcgis/core/layers/TileLayer.js";
const viewElement = document.querySelector("arcgis-scene");
// Set Mars spatial reference
viewElement.spatialReference = { wkid: 104971 }; // Mars 2000
// Configure camera for Mars
viewElement.camera = {
position: {
x: 27.63423,
y: -6.34466,
z: 1281525,
spatialReference: { wkid: 104971 }
},
heading: 332,
tilt: 37
};
await viewElement.viewOnReady();
// Mars elevation
const marsElevation = new ElevationLayer({
url: "https://astro.arcgis.com/arcgis/rest/services/OnMars/MDEM200M/ImageServer"
});
viewElement.ground = { layers: [marsElevation] };
// Mars imagery
const marsImagery = new TileLayer({
url: "https://astro.arcgis.com/arcgis/rest/services/OnMars/MDIM/MapServer",
title: "Mars Imagery"
});
viewElement.map.add(marsImagery);
</script><arcgis-scene basemap="hybrid" ground="world-elevation">
<arcgis-zoom slot="top-left"></arcgis-zoom>
<!-- Embed overview map inside scene -->
<arcgis-map basemap="topo-vector" id="overviewDiv" slot="top-right"></arcgis-map>
</arcgis-scene>
<style>
#overviewDiv { width: 300px; height: 200px; border: 1px solid black; }
</style>
<script type="module">
import Graphic from "@arcgis/core/Graphic.js";
import * as reactiveUtils from "@arcgis/core/core/reactiveUtils.js";
const sceneElement = document.querySelector("arcgis-scene");
const overviewElement = document.querySelector("arcgis-map");
await sceneElement.viewOnReady();
await overviewElement.viewOnReady();
// Disable rotation on overview
overviewElement.constraints.rotationEnabled = false;
overviewElement.view.ui.components = [];
// Add visible area graphic
const visibleAreaGraphic = new Graphic({
symbol: {
type: "simple-fill",
color: [0, 0, 0, 0.5],
outline: null
}
});
overviewElement.graphics.add(visibleAreaGraphic);
// Sync overview with main scene
reactiveUtils.watch(
() => sceneElement.visibleArea,
async (visibleArea) => {
visibleAreaGraphic.geometry = visibleArea;
await overviewElement.goTo(visibleArea);
},
{ initial: true }
);
</script>import * as promiseUtils from "@arcgis/core/core/promiseUtils.js";
// Create debounced function
const debouncedUpdate = promiseUtils.debounce(async () => {
// This only runs after 300ms of no calls
await updateFeatures();
});
view.on("pointer-move", () => {
debouncedUpdate();
});import * as promiseUtils from "@arcgis/core/core/promiseUtils.js";
const abortController = new AbortController();
try {
await someAsyncOperation({ signal: abortController.signal });
} catch (error) {
if (promiseUtils.isAbortError(error)) {
// Operation was intentionally cancelled
console.log("Operation cancelled");
} else {
// Real error
throw error;
}
}
// Cancel the operation
abortController.abort();import * as reactiveUtils from "@arcgis/core/core/reactiveUtils.js";
// Watch single property
reactiveUtils.watch(
() => view.scale,
(scale) => console.log("Scale:", scale)
);
// Watch with initial value
reactiveUtils.watch(
() => view.extent,
(extent) => console.log("Extent:", extent),
{ initial: true }
);
// Watch once
reactiveUtils.once(
() => view.stationary === true
).then(() => {
console.log("View became stationary");
});
// When condition becomes true
reactiveUtils.when(
() => layer.loaded,
() => console.log("Layer loaded")
);intro-mapview - Basic MapView setup and configurationintro-sceneview - Basic SceneView setup for 3Dwebmap-basic - Loading a WebMap from portalwatch-for-changes-reactiveutils - Reactive property watching with reactiveUtilsoverview-map - Creating an overview/inset maparcgis-layers for adding data layers to maps.arcgis-starter-app for project setup and scaffolding.arcgis-core-utilities for reactiveUtils and promiseUtils details.arcgis-interaction for hit testing, popups, and user interaction.Missing CSS for Core API: The Core API requires main.css for widgets and popups to render correctly.
<!-- Anti-pattern: no CSS import -->
<script type="module">
import MapView from "@arcgis/core/views/MapView.js";
import Map from "@arcgis/core/Map.js";
const view = new MapView({ container: "viewDiv", map: new Map({ basemap: "topo-vector" }) });
</script><!-- Correct: include the CSS -->
<link rel="stylesheet" href="https://js.arcgis.com/5.0/esri/themes/light/main.css" />
<script type="module">
import MapView from "@arcgis/core/views/MapView.js";
import Map from "@arcgis/core/Map.js";
const view = new MapView({ container: "viewDiv", map: new Map({ basemap: "topo-vector" }) });
</script>Impact: The map itself renders, but widgets (Zoom, Legend, Search) and popups appear unstyled or completely broken. Layouts collapse and controls become unusable.
Not awaiting viewOnReady(): View properties are not available until the view is ready.
// Anti-pattern: accessing view before it is ready
const mapElement = document.querySelector("arcgis-map");
const view = mapElement.view; // undefined - view is not ready yet
view.goTo({ center: [-118, 34] }); // TypeError: Cannot read properties of undefined// Correct: wait for the view to be ready
const mapElement = document.querySelector("arcgis-map");
await mapElement.viewOnReady();
const view = mapElement.view; // MapView instance, safe to use
view.goTo({ center: [-118, 34] });Impact: view is null or undefined before the component initializes, causing runtime errors on any property access or method call.
Coordinate order: ArcGIS uses [longitude, latitude], not [latitude, longitude].
// Anti-pattern: lat/lng order (Google Maps convention)
const view = new MapView({
center: [34.05, -118.24], // lat first, lng second - WRONG
zoom: 12
});// Correct: lng/lat order (ArcGIS convention)
const view = new MapView({
center: [-118.24, 34.05], // lng first, lat second
zoom: 12
});Impact: The map centers on the wrong location, often in the middle of the ocean or on a different continent, with no error message to indicate the mistake.
Missing viewDiv height: Ensure the container has height:
html, body, #viewDiv { height: 100%; margin: 0; }Script type: Use type="module" for async/await support:
<script type="module">
// async/await works here
</script>d84407b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.