Install
$ agentstack add skill-saschabrunnerch-arcgis-maps-sdk-js-ai-context-arcgis-widgets-ui ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
ArcGIS Widgets & UI
Use this skill when building user interfaces with widgets, Map Components, and Calcite.
> Best Practice: Prefer Map Components (web components like arcgis-legend, arcgis-search) over Core API widgets. Esri has transitioned to web components as the primary approach.
Import Patterns
Map Components (ESM)
import "@arcgis/map-components/dist/components/arcgis-map";
import "@arcgis/map-components/dist/components/arcgis-legend";
import "@arcgis/map-components/dist/components/arcgis-search";
import "@arcgis/map-components/dist/components/arcgis-layer-list";
import "@arcgis/map-components/dist/components/arcgis-expand";
Core API Widgets (ESM)
import Legend from "@arcgis/core/widgets/Legend.js";
import LayerList from "@arcgis/core/widgets/LayerList.js";
import Search from "@arcgis/core/widgets/Search.js";
import BasemapGallery from "@arcgis/core/widgets/BasemapGallery.js";
Dynamic Imports (CDN)
const Legend = await $arcgis.import("@arcgis/core/widgets/Legend.js");
const Search = await $arcgis.import("@arcgis/core/widgets/Search.js");
> Note: CSS for Map Components loads automatically via npm. Core API widgets require: @import "@arcgis/core/assets/esri/themes/light/main.css";
Map Components Overview
| Component | Purpose | | ----------------------------------- | ---------------------------------- | | arcgis-map | 2D map container | | arcgis-scene | 3D scene container | | arcgis-zoom | Zoom in/out buttons | | arcgis-compass | Orientation indicator | | arcgis-home | Return to initial extent | | arcgis-locate | Find user location | | arcgis-track | Track user location | | arcgis-navigation-toggle | Pan/rotate mode (3D) | | arcgis-floor-filter | Filter indoor map by floor level | | arcgis-fullscreen | Toggle fullscreen | | arcgis-scale-bar | Display map scale | | arcgis-legend | Layer symbology legend | | arcgis-layer-list | Layer visibility control | | arcgis-basemap-gallery | Switch basemaps | | arcgis-basemap-layer-list | Layer list for basemap layers | | arcgis-basemap-toggle | Toggle two basemaps | | arcgis-catalog-layer-list | Browse CatalogLayer sublayers | | arcgis-search | Location search | | arcgis-placement | Control widget placement | | arcgis-popup | Feature popups | | arcgis-editor | Feature editing | | arcgis-feature | Display feature info without popup | | arcgis-feature-form | Form-based attribute editing | | arcgis-features | Display multiple features info | | arcgis-sketch | Draw geometries | | arcgis-feature-table | Tabular data view | | arcgis-time-slider | Temporal navigation | | arcgis-expand | Collapsible container | | arcgis-print | Map printing | | arcgis-table-list | List and manage feature tables | | arcgis-bookmarks | Navigate to bookmarks | | arcgis-directions | Turn-by-turn routing | | arcgis-swipe | Compare layers | | arcgis-coordinate-conversion | Coordinate formats | | arcgis-daylight | 3D lighting control | | arcgis-weather | 3D weather effects | | arcgis-distance-measurement-2d | 2D distance measurement | | arcgis-area-measurement-2d | 2D area measurement | | arcgis-direct-line-measurement-3d | 3D line measurement | | arcgis-area-measurement-3d | 3D area measurement | | arcgis-elevation-profile | Elevation profile along a path | | arcgis-line-of-sight | Line of sight analysis (3D) | | arcgis-slice | Slice through 3D data | | arcgis-shadow-cast | Shadow cast analysis (3D) | | arcgis-oriented-imagery-viewer | View oriented imagery | | arcgis-video-player | Play video feeds | | arcgis-link-chart | Link chart visualization | | arcgis-utility-network-trace | Utility network tracing | | arcgis-version-management | Manage geodatabase versions |
Slot-Based Positioning
Available slots: top-left, top-right, bottom-left, bottom-right, top-start, top-end, bottom-start, bottom-end, popup
Widgets in the same slot stack vertically in DOM order.
Expand Component
Wrap widgets in arcgis-expand for collapsible behavior:
Reference Element (External Components)
Place components outside the map and reference them:
Common Widgets
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");
LayerList
// 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");
BasemapGallery
// Core API
import BasemapGallery from "@arcgis/core/widgets/BasemapGallery.js";
const basemapGallery = new BasemapGallery({ view: view });
view.ui.add(basemapGallery, "top-right");
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);
});
FeatureTable
// 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,
},
tableTemplate: {
columnTemplates: [
{ fieldName: "name", label: "Name" },
{ fieldName: "population", label: "Population" },
],
},
});
// Watch selection via highlightIds
featureTable.highlightIds.on("change", (event) => {
console.log("Selected IDs:", featureTable.highlightIds.toArray());
});
TimeSlider
const timeSlider = document.querySelector("arcgis-time-slider");
const mapEl = document.querySelector("arcgis-map");
await mapEl.viewOnReady();
await layer.load();
timeSlider.fullTimeExtent = layer.timeInfo.fullTimeExtent;
timeSlider.stops = { interval: layer.timeInfo.interval };
// 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,
loop: true,
});
view.ui.add(timeSlider, "bottom-right");
// Events
import * as reactiveUtils from "@arcgis/core/core/reactiveUtils.js";
reactiveUtils.watch(
() => timeSlider.timeExtent,
(timeExtent) => {
console.log("Time changed:", timeExtent.start, timeExtent.end);
},
);
Core Widget Approach
Adding Widgets to View
import Legend from "@arcgis/core/widgets/Legend.js";
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 at specific index (order in position)
view.ui.add(legend, { position: "bottom-left", index: 0 });
// Remove widget
view.ui.remove(legend);
Widget in Custom Container
import Legend from "@arcgis/core/widgets/Legend.js";
const legend = new Legend({
view: view,
container: "legendDiv",
});
Calcite Design System Integration
Basic Layout with Calcite
html,
body {
height: 100%;
margin: 0;
}
Calcite Action Bar
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;
});
});
});
Common Calcite Components
| Component | Purpose | | --------------------- | -------------------- | | calcite-shell | App layout container | | calcite-shell-panel | Side panels | | calcite-panel | Content panel | | calcite-navigation | Header/footer | | calcite-action-bar | Icon button bar | | calcite-action | Icon button | | calcite-button | Standard button | | calcite-input | Text input | | calcite-list | List container | | calcite-list-item | List item | | calcite-card | Card container | | calcite-modal | Modal dialog | | calcite-alert | Alert message | | calcite-loader | Loading indicator |
Theming
:root {
--calcite-color-brand: #007ac2;
--calcite-color-brand-hover: #005a8e;
--calcite-color-text-1: #323232;
}
Widget Events
// 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);
});
// FeatureTable selection
featureTable.highlightIds.on("change", (event) => {
console.log(event.added, event.removed);
});
Reference Samples
legend- Legend widget for layer symbologywidgets-layerlist- LayerList widget for layer managementwidgets-search-multiplesource- Search widget with multiple sourceswidgets-featuretable- FeatureTable widget integrationbasemap-gallery- BasemapGallery for switching basemapswidgets-timeslider- TimeSlider widget for temporal data
Common Pitfalls
- Missing reference-element: Components placed outside the `` tag cannot find the view without an explicit reference.
```html
```
```html
```
Impact: The component cannot discover the associated view. It either does not render or throws an error.
- Slot names are specific: Use exact slot names (
top-left, nottopleft).
- Calcite CSS not loading: Ensure Calcite script is loaded before using Calcite components.
- Widget container conflicts: Do not add the same widget to both a DOM container and
view.ui.
``javascript // Anti-pattern: widget in both container and view.ui const legend = new Legend({ view: view, container: "legendDiv" }); view.ui.add(legend, "bottom-right"); // Conflicts ``
``javascript // Correct: pick one placement strategy const legend = new Legend({ view: view }); view.ui.add(legend, "bottom-right"); ``
Impact: The widget renders twice or the layout breaks.
- Dark/light mode mismatch: Add
calcite-mode-lightorcalcite-mode-darkclass to body.
- Core API widgets missing CSS: When using Core API widgets (not Map Components), you must import
@arcgis/core/assets/esri/themes/light/main.css. Map Components handle CSS automatically.
Related Skills
- See
arcgis-widgets-advancedfor specialized widgets (BuildingExplorer, FloorFilter, Track, Locate, etc.) - See
arcgis-map-toolsfor measurement, print, directions, and swipe tools - See
arcgis-editingfor Editor and Sketch widgets - See
arcgis-tables-formsfor FeatureTable and FeatureForm details
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: SaschaBrunnerCH
- Source: SaschaBrunnerCH/arcgis-maps-sdk-js-ai-context
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.