This guide walks through the full process — from installing Experience Builder to having a working custom widget — using Claude (the app) for design and Claude Code for implementation.
Download and install Node.js 22 LTS from https://nodejs.org/
Verify:
node --version # Should show v22.x.x
npm --version # Should show 10.x.x- Go to https://developers.arcgis.com/experience-builder/
- Sign in with your ArcGIS Developer or organizational account (needs Creator or Professional user type)
- Download the Developer Edition ZIP (version 1.19)
Extract the ZIP to a dedicated development directory. Avoid paths with spaces.
Recommended locations:
Windows:
C:\Dev\ArcGISExperienceBuilder\
Mac/Linux:
~/Dev/ArcGISExperienceBuilder/
After extraction you should see:
ArcGISExperienceBuilder/
├── client/
│ ├── your-extensions/ ← Your custom widgets will go here
│ │ ├── manifest.json ← Extension repo manifest (already exists)
│ │ └── widgets/ ← Widget folders go here
│ ├── dist/
│ ├── package.json
│ └── tsconfig.json
├── server/
│ ├── package.json
│ └── ...
└── ...
Open two terminal windows. You'll keep both open throughout development.
Terminal 1 — Server (install first):
cd ArcGISExperienceBuilder/server
npm ciTerminal 2 — Client (install second):
cd ArcGISExperienceBuilder/client
npm cinpm ci does a clean install from the lockfile. Takes a few minutes the first time.
Start the server FIRST, then the client. The client connects to the server, so the server needs to be running.
Terminal 1:
cd ArcGISExperienceBuilder/server
npm startTerminal 2:
cd ArcGISExperienceBuilder/client
npm startOpen the URL shown in Terminal 1 output (check carefully — port varies by version, usually 3000 or 3001). Accept the self-signed certificate warning. Sign in with your ArcGIS organizational account.
You should see the Experience Builder app gallery. Create a quick test app — add a Map widget, point it at a web map, and preview it to confirm the environment works.
Leave both terminals running. You'll restart the client service when adding new widget folders, but the server stays up.
Confirm client/your-extensions/manifest.json exists and looks like:
{
"name": "your-extensions",
"type": "exb-web-extension-repo",
"description": "Custom widgets",
"version": "1.0.0"
}Confirm client/your-extensions/widgets/ directory exists (may be empty — that's fine).
Confirm client/tsconfig.json includes "your-extensions" in its include array.
If not, add it.
Copy the CLAUDE.md file into the ExB project root:
ArcGISExperienceBuilder/
├── CLAUDE.md ← Place here (project root)
├── client/
├── server/
└── ...
Claude Code reads this file automatically when you open it in this directory. It contains the import rules, manifest conventions, compatibility matrix, common errors, and development workflow.
Create a docs/ folder in the project root and place CustomWidgetDeveloperGuide.md there:
ArcGISExperienceBuilder/
├── CLAUDE.md
├── docs/
│ └── CustomWidgetDeveloperGuide.md ← Place here
├── client/
├── server/
└── ...
The CLAUDE.md already references this file and tells Claude Code to read it before starting widget work.
cd ArcGISExperienceBuilder
git clone https://github.com/Esri/arcgis-experience-builder-sdk-resources.git samplesThis gives Claude Code ~40 real working examples to reference when writing code.
ArcGISExperienceBuilder/
├── CLAUDE.md # Claude Code reads this automatically
├── docs/
│ └── CustomWidgetDeveloperGuide.md # Comprehensive patterns & examples
├── samples/ # (Optional) Esri sample widgets
│ └── widgets/
│ ├── starter-widget/
│ ├── get-map-coordinates-function/
│ └── ...40+ more samples
├── client/
│ ├── your-extensions/
│ │ ├── manifest.json # Extension repo manifest
│ │ └── widgets/ # YOUR widgets go here
│ ├── tsconfig.json
│ └── package.json
├── server/
│ └── ...
└── ...
Your environment is now ready.
Copy the starter prompt from starter-prompt-for-claude-app.md into a new Claude
conversation. This prompt gives Claude the technical context (ExB 1.19, JSAPI 4.34,
React 19, Jimu conventions) and tells it exactly what to produce.
Add your widget description at the bottom. Be specific about:
- What the widget does from the user's perspective
- What data it interacts with (maps, layers, services)
- What the settings panel should let admins configure
- Any external APIs or services it calls
Example for the smart-filter widget:
I want to build a widget called "smart-filter" that takes plain text input from a user (like "parcels larger than 5 acres near downtown") and converts it to an ArcGIS where clause, then applies that filter to the appropriate layer(s) on the map.
The widget should:
- Have a text input where the user types a natural language query
- Parse that text to generate a valid SQL where clause
- Identify which layer(s) the query applies to based on field names
- Apply the filter to the correct layer's definitionExpression on the map
- Show the user what where clause was generated so they can verify/edit it
- Have a clear/reset button to remove filters
- Have a settings panel where the admin selects which map widget to connect to and configures which layers/fields are available for filtering
Claude will produce a design document with architecture, file structure, APIs, an implementation plan, JSAPI deprecation flags, and edge cases.
Review carefully. Key things to check:
- Does the file structure match ExB conventions?
- Are the Jimu API choices correct?
- Are there technical decisions you want to change?
- Does the implementation plan build progressively (minimal → full)?
- Any JSAPI classes flagged as uncertain — verify these
Iterate until the plan is solid. Save the final plan text — you'll paste it into Claude Code.
cd ArcGISExperienceBuilder
claudeClaude Code automatically reads CLAUDE.md and knows it's in an ExB project.
Paste something like this, followed by your plan:
I want to build a custom Experience Builder widget. Here's my reviewed design plan.
Before starting:
1. Read CLAUDE.md (you should have already)
2. Read docs/CustomWidgetDeveloperGuide.md for detailed patterns
3. Check the compatibility matrix — we're on ExB 1.19, JSAPI 4.34, React 19
4. Watch for JSAPI deprecations in 4.34
Build this progressively:
- Start with a minimal widget that just renders a text input (verify it loads)
- Then add the core filtering logic
- Then add the settings panel
- Then add polish (error handling, loading states, etc.)
The plan is in PLAN.MD
Review the plan, ask questions and then build your plan.
Claude Code will create the widget files. After it creates the initial set:
- Stop the client service (Ctrl+C in Terminal 2)
- Restart it (
npm startin client/) — necessary because new widget folder was added - Open ExB builder at the URL shown in Terminal 1
- Create a new app or open an existing one
- Look for your widget in the widget panel — it should appear under custom/organization widgets
- Add it to the app and preview
If the widget appears and renders your basic text → you're in great shape. Tell Claude Code to proceed to the next step in the plan.
If it doesn't appear → check these in order:
- Is the folder name exactly matching
namein manifest.json? - Is
client/tsconfig.jsonincludingyour-extensions? - Did you restart the client service?
- Check Terminal 2 for TypeScript compilation errors
- Check browser console (F12) for runtime errors
This is the core development loop:
- Claude Code adds functionality (hooks, components, logic)
- You check the browser — does it still work?
- If yes → continue to next feature
- If no → check browser console, report the error to Claude Code, it fixes it
You do NOT need to restart the client for code changes within existing files. Webpack hot-reloads those automatically. Just refresh the browser.
You DO need to restart the client if Claude Code:
- Creates new files or folders
- Renames files or folders
- Edits manifest.json
- Installs npm packages
Once Claude Code has built the settings panel:
- In the ExB builder, click on your widget to select it
- The settings panel should appear on the right side
- Configure it (select a map widget, choose layers, etc.)
- Switch to preview mode and verify the widget works with the configuration
If the settings panel doesn't appear:
- Make sure
src/setting/setting.tsxexists - Make sure manifest
propertiesis{}(empty) — let ExB auto-detect - Restart the client service
Once core functionality works, ask Claude Code for:
- Error handling for edge cases (no data, network errors, empty results)
- Loading states and user feedback
- Graceful handling of unconfigured state
- Accessibility (ARIA labels, keyboard navigation)
- Theme-aware styling using Jimu theme variables
- Any i18n translations needed
cd ArcGISExperienceBuilder/client
npm run build:prodOutput goes to client/dist-prod/widgets/your-widget-name/.
The production build includes your compiled widget code, bundled external libraries (like d3 if used), CSS, and all assets — but NOT jimu-, react, or esri/ (those are loaded as externals at runtime).
Option A — Standalone hosting: Deploy the full built ExB app to your web server (includes your widgets).
Option B — ArcGIS Enterprise (11.0+):
Requirements:
- HTTPS web server accessible by Portal
- CORS headers allowing your Portal domain
- JSON MIME type configured
- Anonymous access enabled for the widget files
- Copy
dist-prod/widgets/your-widget-name/to your web server - Configure CORS and MIME types (see IIS example below)
- In Portal: My Content → Add Item → Application → Experience Builder Widget
- Enter manifest URL:
https://your-server.com/widgets/widget-name/manifest.json - Share with your organization
- Widget appears in the Custom group in ExB
IIS web.config example (place in widget hosting directory):
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin"
value="https://your-portal.domain.com" />
<add name="Access-Control-Allow-Methods" value="GET, OPTIONS" />
</customHeaders>
</httpProtocol>
<staticContent>
<mimeMap fileExtension=".json" mimeType="application/json" />
</staticContent>
</system.webServer>
</configuration>Version compatibility note: If your Enterprise is 11.5 (ExB 1.17), make sure your
manifest has "exbVersion": "1.17.0" even if you developed with ExB 1.19.
Option C — Distribution to other developers:
Package the widget folder (source) for others to drop into their ExB your-extensions/widgets/ directory.
| Task | Command |
|---|---|
| Start server (first) | cd server && npm start |
| Start client (second) | cd client && npm start |
| Build production | cd client && npm run build:prod |
| Open Claude Code | cd ArcGISExperienceBuilder && claude |
| Install external lib | cd client && npm install <package> --save |
| Clone samples | git clone https://github.com/Esri/arcgis-experience-builder-sdk-resources.git samples |
RESTART the client service after:
- Adding or removing a widget folder
- Renaming any file or folder
- Editing any
manifest.json - Installing new npm packages
- Changing
tsconfig.json
NO RESTART needed for:
- Editing
.tsx,.ts,.cssfiles within an existing widget (hot reload handles it) - Changing
config.jsonvalues - Editing translation files
| Problem | Likely Cause | Fix |
|---|---|---|
| Widget not in builder panel | Manifest error or client not restarted | Check manifest.json syntax, restart client |
can't access property "default", t is null |
Importing React/hooks from 'react' |
Import from 'jimu-core' instead |
Unknown dependency: jimu-core |
jimu-core listed in manifest dependency array |
Remove it — only jimu-arcgis belongs there |
Module not found: jimu-core/react |
Importing from jimu-core/react |
Import from jimu-core only |
| Settings panel doesn't show | Over-specified manifest properties | Use "properties": {} (empty) |
| Map doesn't connect | Missing dependency in manifest | Add "dependency": ["jimu-arcgis"] |
| TypeScript compilation errors | Extension not in tsconfig | Add to client/tsconfig.json include array |
__set_webpack_public_path__ is null |
Trying to build outside ExB | Always use ExB Dev Edition build system |
| Blank widget in preview | Uncaught error in render | Check browser console (F12) for errors |
| Widget loads but crashes | Null reference on unconfigured state | Add null checks for useDataSources, useMapWidgetIds |
| 404 on Enterprise deploy | Widget server not accessible | Check HTTPS, CORS, JSON MIME type config |
| File | Where to Put It | Purpose |
|---|---|---|
CLAUDE.md |
ExB project root | Auto-read by Claude Code. Import rules, manifest conventions, patterns. |
CustomWidgetDeveloperGuide.md |
docs/ folder |
Comprehensive reference. Full code examples for all widget patterns. |
starter-prompt-for-claude-app.md |
Anywhere (for your use) | Template for designing widgets in Claude app before coding. |
| Esri samples repo | samples/ folder (optional) |
40+ working widget examples Claude Code can reference. |
- React from
jimu-core, never from'react'— in ALL files, not just widget.tsx AllWidgetSettingPropsfromjimu-for-builder, never fromjimu-core- Never put
jimu-coreorjimu-uiin manifest dependency array — onlyjimu-arcgis - Keep manifest
propertiesempty{}— let ExB auto-detect - Config is immutable — use
.set()/.setIn(), never mutate - Start server before client — client connects to server
- Restart client after structural changes — new files, renames, manifest edits
- Build progressively — minimal widget first, add features one at a time, test each step
- Never build outside ExB — its SystemJS/webpack setup can't be replicated
- Plan before coding — design in Claude app, implement in Claude Code
- One widget at a time — finish and verify before starting the next