Loading...
Loading...
Build map user interfaces with ArcGIS widgets, Map Components, and Calcite Design System. Use for adding legends, layer lists, search, tables, time sliders, and custom UI layouts.
npx skill4agent add saschabrunnerch/arcgis-maps-sdk-js-ai-context arcgis-widgets-uiBest Practice: Prefer Map Components (web components like,arcgis-legend) over Core API widgets when possible. Esri is transitioning to web components, and some widgets are already deprecated. See Esri's component transition plan.arcgis-search
| Component | Purpose |
|---|---|
| 2D map container |
| 3D scene container |
| Zoom in/out buttons |
| Orientation indicator |
| Return to initial extent |
| Find user location |
| Track user location |
| Pan/rotate mode (3D) |
| Toggle fullscreen |
| Display map scale |
| Layer symbology legend |
| Layer visibility control |
| Switch basemaps |
| Toggle two basemaps |
| Location search |
| Feature popups |
| Feature editing |
| Draw geometries |
| Tabular data view |
| Temporal navigation |
| Display time zone |
| Collapsible container |
| Map printing |
| Navigate to bookmarks |
| Turn-by-turn routing |
| Compare layers |
| Coordinate formats |
| 3D lighting control |
| 3D weather effects |
| 2D distance measurement |
| 2D area measurement |
| 3D line measurement |
| 3D area measurement |
| Utility network tracing |
| Utility associations |
Note: Not all widgets have component equivalents yet. FeatureForm, Histogram, and some specialized widgets only have Core API versions.
<arcgis-map basemap="streets-vector">
<!-- Position widgets using slots -->
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-home slot="top-left"></arcgis-home>
<arcgis-compass slot="top-left"></arcgis-compass>
<arcgis-search slot="top-right"></arcgis-search>
<arcgis-layer-list slot="top-right"></arcgis-layer-list>
<arcgis-legend slot="bottom-left"></arcgis-legend>
<arcgis-scale-bar slot="bottom-right"></arcgis-scale-bar>
<!-- Popup must use popup slot -->
<arcgis-popup slot="popup"></arcgis-popup>
</arcgis-map>top-lefttop-rightbottom-leftbottom-rightpopupmanualarcgis-expand<arcgis-map basemap="streets-vector">
<arcgis-expand slot="top-right" expand-tooltip="Show Legend" mode="floating">
<arcgis-legend></arcgis-legend>
</arcgis-expand>
<arcgis-expand slot="top-left" expanded>
<arcgis-layer-list></arcgis-layer-list>
</arcgis-expand>
</arcgis-map><calcite-shell>
<calcite-shell-panel slot="panel-start">
<arcgis-legend reference-element="arcgis-map"></arcgis-legend>
</calcite-shell-panel>
<arcgis-map id="arcgis-map" basemap="topo-vector">
<arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-map>
</calcite-shell>import Legend from "@arcgis/core/widgets/Legend.js";
import LayerList from "@arcgis/core/widgets/LayerList.js";
import Search from "@arcgis/core/widgets/Search.js";
// Create widget
const legend = new Legend({ view: view });
// Add to view UI
view.ui.add(legend, "bottom-left");
// Add multiple widgets
view.ui.add([
{ component: legend, position: "bottom-left" },
{ component: search, position: "top-right" }
]);
// Add to specific index (order in position)
view.ui.add(legend, { position: "bottom-left", index: 0 });
// Remove widget
view.ui.remove(legend);<div id="legendDiv"></div>
<script type="module">
import Legend from "@arcgis/core/widgets/Legend.js";
const legend = new Legend({
view: view,
container: "legendDiv" // Or document.getElementById("legendDiv")
});
</script><!-- Map Component -->
<arcgis-legend slot="bottom-left"></arcgis-legend>// Core API
import Legend from "@arcgis/core/widgets/Legend.js";
const legend = new Legend({
view: view,
layerInfos: [{
layer: featureLayer,
title: "Custom Title"
}]
});
view.ui.add(legend, "bottom-left");<!-- Map Component -->
<arcgis-layer-list slot="top-right"></arcgis-layer-list>// Core API with actions
import LayerList from "@arcgis/core/widgets/LayerList.js";
const layerList = new LayerList({
view: view,
listItemCreatedFunction: (event) => {
const item = event.item;
item.actionsSections = [[{
title: "Zoom to layer",
icon: "zoom-to-object",
id: "zoom-to"
}]];
}
});
layerList.on("trigger-action", (event) => {
if (event.action.id === "zoom-to") {
view.goTo(event.item.layer.fullExtent);
}
});
view.ui.add(layerList, "top-right");<!-- Map Component -->
<arcgis-basemap-gallery slot="top-right"></arcgis-basemap-gallery>// Core API
import BasemapGallery from "@arcgis/core/widgets/BasemapGallery.js";
const basemapGallery = new BasemapGallery({
view: view
});
view.ui.add(basemapGallery, "top-right");<!-- Map Component -->
<arcgis-search slot="top-right"></arcgis-search>// Core API with custom sources
import Search from "@arcgis/core/widgets/Search.js";
const search = new Search({
view: view,
sources: [{
layer: featureLayer,
searchFields: ["name", "address"],
displayField: "name",
exactMatch: false,
outFields: ["*"],
name: "My Layer",
placeholder: "Search features"
}]
});
view.ui.add(search, "top-right");
// Events
search.on("select-result", (event) => {
console.log("Selected:", event.result);
});<!-- Map Component -->
<arcgis-feature-table reference-element="arcgis-map"></arcgis-feature-table>// Core API
import FeatureTable from "@arcgis/core/widgets/FeatureTable.js";
const featureTable = new FeatureTable({
view: view,
layer: featureLayer,
container: "tableDiv",
visibleElements: {
header: true,
columnMenus: true,
selectionColumn: true
},
fieldConfigs: [
{ name: "name", label: "Name" },
{ name: "population", label: "Population" }
]
});
// Selection events
featureTable.on("selection-change", (event) => {
console.log("Selected rows:", event.added);
});<!-- Map Component -->
<arcgis-time-slider
slot="bottom-right"
layout="auto"
mode="time-window"
time-visible
loop>
</arcgis-time-slider>
<script type="module">
const timeSlider = document.querySelector("arcgis-time-slider");
await layer.load();
timeSlider.fullTimeExtent = layer.timeInfo.fullTimeExtent;
timeSlider.stops = {
interval: layer.timeInfo.interval
};
</script>// Core API
import TimeSlider from "@arcgis/core/widgets/TimeSlider.js";
const timeSlider = new TimeSlider({
view: view,
mode: "time-window", // instant, time-window, cumulative-from-start, cumulative-from-end
fullTimeExtent: layer.timeInfo.fullTimeExtent,
stops: {
interval: {
value: 1,
unit: "hours"
}
},
playRate: 1000, // ms between stops
loop: true
});
view.ui.add(timeSlider, "bottom-right");
// Events
timeSlider.watch("timeExtent", (timeExtent) => {
console.log("Time changed:", timeExtent.start, timeExtent.end);
});<!-- Map Component -->
<arcgis-print slot="top-right"></arcgis-print>// Core API
import Print from "@arcgis/core/widgets/Print.js";
const print = new Print({
view: view,
printServiceUrl: "https://utility.arcgisonline.com/arcgis/rest/services/Utilities/PrintingTools/GPServer/Export%20Web%20Map%20Task"
});
view.ui.add(print, "top-right");<!DOCTYPE html>
<html>
<head>
<script type="module" src="https://js.arcgis.com/calcite-components/3.3.3/calcite.esm.js"></script>
<script src="https://js.arcgis.com/4.34/"></script>
<script type="module" src="https://js.arcgis.com/4.34/map-components/"></script>
<style>
html, body { height: 100%; margin: 0; }
</style>
</head>
<body class="calcite-mode-light">
<calcite-shell>
<!-- Header -->
<calcite-navigation slot="header">
<calcite-navigation-logo slot="logo" heading="My Map App"></calcite-navigation-logo>
</calcite-navigation>
<!-- Side Panel -->
<calcite-shell-panel slot="panel-start">
<calcite-panel heading="Layers">
<arcgis-layer-list reference-element="map"></arcgis-layer-list>
</calcite-panel>
</calcite-shell-panel>
<!-- Map -->
<arcgis-map id="map" basemap="streets-vector">
<arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-map>
<!-- End Panel -->
<calcite-shell-panel slot="panel-end">
<calcite-panel heading="Legend">
<arcgis-legend reference-element="map"></arcgis-legend>
</calcite-panel>
</calcite-shell-panel>
</calcite-shell>
</body>
</html><calcite-shell>
<calcite-shell-panel slot="panel-start">
<calcite-action-bar slot="action-bar">
<calcite-action icon="layers" text="Layers" data-panel="layers"></calcite-action>
<calcite-action icon="legend" text="Legend" data-panel="legend"></calcite-action>
<calcite-action icon="bookmark" text="Bookmarks" data-panel="bookmarks"></calcite-action>
</calcite-action-bar>
<calcite-panel id="layers" heading="Layers">
<arcgis-layer-list reference-element="map"></arcgis-layer-list>
</calcite-panel>
<calcite-panel id="legend" heading="Legend" hidden>
<arcgis-legend reference-element="map"></arcgis-legend>
</calcite-panel>
</calcite-shell-panel>
<arcgis-map id="map" basemap="topo-vector"></arcgis-map>
</calcite-shell>
<script>
// Toggle panels on action click
document.querySelectorAll("calcite-action").forEach(action => {
action.addEventListener("click", () => {
const panelId = action.dataset.panel;
document.querySelectorAll("calcite-panel").forEach(panel => {
panel.hidden = panel.id !== panelId;
});
});
});
</script>| Component | Purpose |
|---|---|
| App layout container |
| Side panels |
| Content panel |
| Header/footer |
| Icon button bar |
| Icon button |
| Standard button |
| Text input |
| List container |
| List item |
| Card container |
| Modal dialog |
| Alert message |
| Loading indicator |
<!-- Light mode -->
<body class="calcite-mode-light">
<!-- Dark mode -->
<body class="calcite-mode-dark">
<!-- Custom theme colors -->
<style>
:root {
--calcite-color-brand: #007ac2;
--calcite-color-brand-hover: #005a8e;
--calcite-color-text-1: #323232;
}
</style>// Add widget at specific position
view.ui.add(widget, {
position: "manual",
index: 0
});
// Position with CSS
document.getElementById("myWidget").style.cssText = `
position: absolute;
top: 10px;
left: 50%;
transform: translateX(-50%);
`;<div id="mapDiv" style="position: relative;">
<div id="customWidget" style="position: absolute; top: 10px; right: 10px; z-index: 1;">
<!-- Custom content -->
</div>
</div>// Search select
search.on("select-result", (event) => {
console.log(event.result);
});
// LayerList trigger action
layerList.on("trigger-action", (event) => {
console.log(event.action, event.item);
});
// TimeSlider time change
timeSlider.watch("timeExtent", (value) => {
console.log(value.start, value.end);
});
// FeatureTable selection
featureTable.on("selection-change", (event) => {
console.log(event.added, event.removed);
});typeas const// Use 'as const' for widget configurations
const layerList = new LayerList({
view: view,
listItemCreatedFunction: (event) => {
const item = event.item;
item.actionsSections = [[{
title: "Zoom to layer",
icon: "zoom-to-object",
id: "zoom-to"
}]];
}
});
// For layer configurations in widgets
const featureTable = new FeatureTable({
view: view,
layer: featureLayer,
fieldConfigs: [
{ name: "name", label: "Name" },
{ name: "population", label: "Population" }
]
});Tip: See arcgis-core-maps skill for detailed guidance on autocasting vs explicit classes.
reference-elementtop-lefttopleftcalcite-mode-lightcalcite-mode-dark