Install
$ agentstack add skill-saschabrunnerch-arcgis-maps-sdk-js-ai-context-arcgis-starter-app ✓ 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 Used
- ✓ 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 Starter App
Use this skill to create ArcGIS Maps SDK for JavaScript applications with TypeScript and Vite. Choose between a minimal setup for quick prototyping or a production-ready configuration with full tooling.
Prerequisites
- ArcGIS account: An ArcGIS Location Platform, ArcGIS Online, or ArcGIS Enterprise account is required to access content and services.
- Access token: Create an access token through your portal with appropriate privileges (basemaps, geocoding, routing, etc.) and configure allowed referrers for production use.
- Node.js: LTS version (20+) with npm or pnpm.
> Note: CSS for Map Components loads automatically when using npm — no manual CSS import for @arcgis/map-components is needed. The @arcgis/core CSS import is only required when using Core API widgets directly.
Framework Integration
React 19
Map Components are web components (custom elements). In React 19, use them directly and attach event listeners with the on prefix:
import "@arcgis/map-components/dist/components/arcgis-map";
import "@arcgis/map-components/dist/components/arcgis-zoom";
function App() {
return (
{
const { view } = e.detail;
console.log("View ready:", view);
}}
>
);
}
> Important: Use event listeners (e.g., onarcgisViewReadyChange) rather than calling methods directly on the element ref, since the component may not be defined yet at render time.
Angular
Add CUSTOM_ELEMENTS_SCHEMA to your module or standalone component so Angular recognizes non-Angular elements:
import { CUSTOM_ELEMENTS_SCHEMA, Component } from "@angular/core";
@Component({
selector: "app-map",
standalone: true,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
`,
})
export class MapComponent {}
Import the Map Components CSS explicitly in your root stylesheet:
/* styles.css */
@import "@arcgis/map-components/dist/components/arcgis-map/arcgis-map.css";
TypeScript Configuration
Set moduleResolution to "node16" or "bundler" in tsconfig.json to properly resolve @arcgis/map-components package exports:
{
"compilerOptions": {
"moduleResolution": "bundler"
}
}
Minimal Setup
Project Structure
my-arcgis-app/
├── src/
│ ├── main.ts
│ └── style.css
├── index.html
├── package.json
├── tsconfig.json
├── vite.config.ts
├── .gitignore
└── README.md
package.json
{
"name": "my-arcgis-app",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"devDependencies": {
"typescript": "~5.9.3",
"vite": "^7.2.7"
},
"dependencies": {
"@arcgis/map-components": "^5.0.0",
"@esri/calcite-components": "^3.3.3"
}
}
index.html (2D Map)
ArcGIS Map App
index.html (3D Scene)
ArcGIS Scene App
src/main.ts (Map Components)
import "@arcgis/map-components/dist/components/arcgis-map";
import "@arcgis/map-components/dist/components/arcgis-zoom";
import "@arcgis/map-components/dist/components/arcgis-legend";
// For 3D scenes, also import:
// import "@arcgis/map-components/dist/components/arcgis-scene";
// import "@arcgis/map-components/dist/components/arcgis-navigation-toggle";
// import "@arcgis/map-components/dist/components/arcgis-compass";
import "@esri/calcite-components/dist/components/calcite-shell";
import { setAssetPath as setCalciteAssetPath } from "@esri/calcite-components/dist/components";
// Set Calcite assets path
setCalciteAssetPath("https://js.arcgis.com/calcite-components/3.3.3/assets");
// Configure ArcGIS API key
import esriConfig from "@arcgis/core/config";
esriConfig.apiKey = "YOUR_API_KEY";
// Wait for map to be ready
const arcgisMap = document.querySelector("arcgis-map");
arcgisMap?.addEventListener("arcgisViewReadyChange", (event) => {
const { view } = (event as CustomEvent).detail;
console.log("Map view ready:", view);
});
src/main.ts (Core API - Programmatic)
import "@esri/calcite-components/dist/components/calcite-shell";
import { setAssetPath as setCalciteAssetPath } from "@esri/calcite-components/dist/components";
import Map from "@arcgis/core/Map.js";
import MapView from "@arcgis/core/views/MapView.js";
// Set Calcite assets path
setCalciteAssetPath("https://js.arcgis.com/calcite-components/3.3.3/assets");
const map = new Map({ basemap: "topo-vector" });
// Create view
const view = new MapView({
container: "viewDiv",
map: map,
center: [-118.805, 34.027],
zoom: 13,
});
src/style.css
@import "@arcgis/core/assets/esri/themes/light/main.css";
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
font-family: "Avenir Next", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
calcite-shell {
width: 100%;
height: 100%;
}
arcgis-map,
arcgis-scene,
#viewDiv {
width: 100%;
height: 100%;
}
tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}
vite.config.ts
import { defineConfig } from "vite";
export default defineConfig({
build: { target: "esnext" },
});
.gitignore
node_modules/
dist/
.env
.env.local
Quick Start
npm install @arcgis/map-components @esri/calcite-components
npm install -D typescript vite
npm run dev
Common Widgets
// Import additional components as needed
import "@arcgis/map-components/dist/components/arcgis-search";
import "@arcgis/map-components/dist/components/arcgis-basemap-gallery";
import "@arcgis/map-components/dist/components/arcgis-layer-list";
Production Setup
Project Structure
my-arcgis-app/
├── .github/
│ └── workflows/
│ ├── test.yml
│ └── deploy.yml
├── src/
│ ├── main.ts
│ └── style.css
├── index.html
├── package.json
├── tsconfig.json
├── vite.config.ts
├── eslint.config.js
├── .prettierrc
├── .prettierignore
├── .editorconfig
├── .gitattributes
├── .gitignore
├── .env.example
└── README.md
package.json
{
"name": "my-arcgis-app",
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"lint": "eslint .",
"format:check": "prettier --check .",
"format": "prettier --write .",
"prepare": "simple-git-hooks"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"eslint": "^9.39.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-unicorn": "^55.0.0",
"globals": "^16.5.0",
"prettier": "^3.7.4",
"lint-staged": "^16.2.7",
"simple-git-hooks": "^2.13.1",
"typescript": "~5.9.3",
"typescript-eslint": "^8.49.0",
"vite": "^7.2.7"
},
"dependencies": {
"@arcgis/core": "5.0.0",
"@arcgis/map-components": "^5.0.0",
"@esri/calcite-components": "^3.3.3"
},
"simple-git-hooks": {
"pre-commit": "pnpm exec lint-staged",
"pre-push": "pnpm exec tsc --noEmit"
},
"lint-staged": {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{js,json,css,md,yml,yaml}": ["prettier --write"]
}
}
eslint.config.js
import eslint from "@eslint/js";
import configPrettier from "eslint-config-prettier";
import importPlugin from "eslint-plugin-import";
import unicornPlugin from "eslint-plugin-unicorn";
import globals from "globals";
import tseslint from "typescript-eslint";
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
// Enable type-aware linting for TypeScript files
{
files: ["**/*.ts", "**/*.tsx"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
parserOptions: {
projectService: true,
allowDefaultProject: ["vite.config.ts"],
},
},
},
// Scope type-checked recommendations strictly to TS files
...tseslint.configs.recommendedTypeChecked.map((cfg) => ({
...cfg,
files: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"],
})),
{
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
...globals.browser,
},
},
},
{
ignores: ["dist/**", "node_modules/**"],
},
{
ignores: ["vite.config.ts"],
},
{
plugins: {
import: importPlugin,
unicorn: unicornPlugin,
},
rules: {
// import hygiene
"import/no-duplicates": "error",
"import/no-useless-path-segments": "error",
"import/no-extraneous-dependencies": [
"error",
{
devDependencies: [
"**/eslint.config.*",
"**/vite.config.*",
"**/*.config.*",
"**/scripts/**",
"**/*.test.*",
"**/*.spec.*",
".github/**",
],
optionalDependencies: false,
peerDependencies: true,
},
],
"import/order": [
"warn",
{
groups: [
"builtin",
"external",
"internal",
"parent",
"sibling",
"index",
"object",
"type",
],
"newlines-between": "always",
alphabetize: { order: "asc", caseInsensitive: true },
},
],
// unused vars/imports
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
// code quality
eqeqeq: ["error", "always"],
"no-console": ["warn", { allow: ["warn", "error"] }],
"@typescript-eslint/no-explicit-any": "warn",
// unicorn essentials
"unicorn/prefer-node-protocol": "error",
"unicorn/prefer-string-starts-ends-with": "error",
"unicorn/prefer-array-find": "error",
"unicorn/throw-new-error": "error",
// typescript consistency
"@typescript-eslint/consistent-type-imports": "warn",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-require-imports": "error",
},
},
// Node environment for config and build scripts
{
files: ["eslint.config.js", "vite.config.*", "*.config.*", "scripts/**"],
languageOptions: {
globals: {
...globals.node,
},
},
},
// Declarations: relax strictness for ambient types
{
files: ["**/*.d.ts"],
rules: {
"@typescript-eslint/no-redundant-type-constituents": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
},
},
// TS-only: enable balanced type-aware rules
{
files: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"],
rules: {
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-misused-promises": [
"error",
{ checksVoidReturn: false },
],
"@typescript-eslint/await-thenable": "warn",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/require-await": "off",
"@typescript-eslint/no-unnecessary-type-assertion": "warn",
"@typescript-eslint/no-redundant-type-constituents": "off",
},
},
// Disable ESLint rules that would conflict with Prettier formatting
configPrettier,
);
.prettierrc
{
"arrowParens": "always",
"bracketSameLine": false,
"bracketSpacing": true,
"endOfLine": "lf",
"jsxSingleQuote": false,
"printWidth": 120,
"quoteProps": "consistent",
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"useTabs": false,
"proseWrap": "preserve",
"htmlWhitespaceSensitivity": "css"
}
.prettierignore
dist/
node_modules/
*.min.js
package-lock.json
pnpm-lock.yaml
.editorconfig
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
.gitattributes
* text=auto eol=lf
.gitignore
node_modules/
dist/
.env
.env.local
.env.*.local
*.log
.DS_Store
*.tsbuildinfo
.env.example
# ArcGIS API Key
# Get your API key from https://developers.arcgis.com/
VITE_ARCGIS_API_KEY=your_arcgis_api_key_here
.github/workflows/test.yml
name: Test
on:
pull_request:
branches:
- main
permissions:
contents: read
issues: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "24"
cache: "pnpm"
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Security audit
run: pnpm audit --audit-level=high
- name: ESLint
run: pnpm run lint
- name: Prettier check
run: pnpm run format:check
- name: Annotate formatting issues
if: failure()
uses: actions/github-script@v7
with:
script: |
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
const body = [
'Prettier check failed. Please run `pnpm run format` locally to fix formatting.',
'',
`Workflow run: ${runUrl}`,
].join('\n');
if (context.payload.pull_request) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body,
});
} else {
core.summary.addHeading('Prettier check failed');
core.summary.addRaw(body);
await core.summary.write();
}
- name: Type check
run: pnpm exec tsc --noEmit
- name: Build
run: pnpm run build
env:
NODE_OPTIONS: "--max-old-space-size=4096"
VITE_ARCGIS_API_KEY: ${{ secrets.VITE_ARCGIS_API_KEY }}
.github/workflows/deploy.yml
name: Build & Deploy to GitHub Pages
on:
push:
branches:
- main
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [SaschaBrunnerCH](https://github.com/SaschaBrunnerCH)
- **Source:** [SaschaBrunnerCH/arcgis-maps-sdk-js-ai-context](https://github.com/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.