0%

The Complete Guide to
Actions & Triggers

How widgets talk to each other. From your first click-to-zoom action to multi-step workflows that power real operational apps.

Beginner — 7 lessons Intermediate — 6 lessons Advanced — 5 lessons

What's inside

Understanding the Action System

Without actions, your Experience Builder app is a collection of independent widgets that ignore each other. Actions are the wiring that makes them work together.

Lesson 0

What are actions and why they matter

You have a Map widget. You have a List widget. You have a Text widget. They all sit on the same page — but right now, they don't know about each other. Click a feature on the map? The List doesn't care. Select a row in the List? The Text widget shows the same static content it always does.

That's the default state of an EB app. Every widget is an island.

Actions are how you build bridges between those islands. They are EB's system for saying: "When this happens in Widget A, do that to Widget B."

Think of it this way

Actions are electrical wiring. Each wire connects an output (a trigger event, like "user clicked a feature") to an input (an action, like "zoom the map here"). If the wire is loose or connected to the wrong terminal, nothing happens. Your job is to be the electrician — connecting the right outputs to the right inputs.

EB uses an event-driven architecture. That sounds technical, but it just means: things happen in response to events. A user clicks something — that's an event. A data source finishes loading — that's an event. A filter changes — that's an event. You decide which events matter and what should happen when they fire.

1.

Trigger

Something happens. A user clicks a feature, selects a row, clicks a button, or the page loads.

2.

Message

The trigger creates a message containing data — which feature was clicked, which record was selected.

3.

Action

A target widget receives the message and does something — zooms, filters, shows content, opens a panel.

User clicks feature
Trigger event
Message created
Contains record data
Target widget acts
Zoom, filter, show

Every action you configure in EB follows this exact pattern. Once you internalize Trigger → Message → Action, everything else is just details about which triggers exist and which actions are available.

Key concept

Actions live on the triggering widget, not the target. This is the single most confusing thing about EB actions. You configure actions in the Action tab of the widget that starts the chain — not the widget that receives the instruction. If you want a Map click to zoom a second Map, you configure the action on the first Map, even though the second Map is the one that moves.

Honest warning

EB's action configuration UI is not intuitive. The panel is small. The labels are vague. The nesting of trigger → action → target → settings is deep. You will lose your place. You will accidentally configure the wrong trigger. This is normal. The concepts are simple — the UI just makes them harder than they need to be. This guide will make it manageable.

Lesson 1

Your first action: Map click to Text update

Let's wire up the simplest possible action: click a feature on the Map → see its name in a Text widget. This teaches you the full action configuration flow that every other action follows.

Before you start

You need an EB app with a Map widget connected to a feature layer (any layer with features you can click) and a Text widget somewhere on the same page. If you don't have one, create a blank EB app, drop in a Map, add a web map with any feature layer, and add a Text widget.

1
Select the Map widget in the builder (click it on the canvas or in the widget tree on the left)
2
In the settings panel on the right, click the Action tab (it's the last tab, with a lightning bolt icon)
3
You'll see a list of trigger events this widget supports. For the Map widget, you'll see triggers like "Record selection changes" and "Extent changes." Click Record selection changes
4
Click + Add action. A dropdown appears with available action types. Select Message action
5
Under "Action target," choose your Text widget from the list of widgets on the page
6
The Text widget will appear as a target. Under its settings, you'll see "Condition: When the record selection changes" — this confirms the wiring
7
Click Live View (the play button in the top toolbar). Click a feature on the map. Your Text widget should now respond to the selection
Map widget
Record selection changes
Message action
Text widget
Shows selected record
Try it

If the Text widget uses Advanced Formatting (Arcade), it can display specific fields from the selected record — like the feature name, a status value, or a count. If it's just a plain Text widget connected to the same data source, it will show the first field. For now, just confirm the wiring works. We'll get fancy later.

Common mistake

You tried to configure the action on the Text widget. Remember: actions live on the trigger widget. You clicked the Text widget, went to its Action tab, and found no "record selection" trigger — because the Text widget doesn't trigger selection events. Go back to the Map widget's Action tab. That's where the trigger lives.

Quick check: Where do you configure an action that fires when a Map feature is clicked?

Lesson 2

Record selection actions

Record selection is the most common trigger in EB. When a user clicks a feature on the map, selects a row in a Table, or taps an item in a List — that's a record selection event. Understanding which widgets can trigger selection and which can receive it is fundamental.

WidgetCan trigger selection?Can receive selection?
MapYes — click/tap a featureYes — highlights the selected feature
ListYes — click/tap a list itemYes — scrolls to and highlights the item
TableYes — click a table rowYes — highlights the selected row
ChartYes — click a bar/slice/pointYes — highlights the segment
TextNoYes — displays selected record data
ImageNoYes — shows image for selected record
ButtonNo (uses "Button click" trigger instead)No

When a selection happens, EB sends a selection message that contains the selected record(s). Any widget wired to receive that message can use the record data — display fields, zoom to geometry, filter related data.

Key concept: Changes vs. Adds

The Map widget has two selection-related triggers:

  • Record selection changes — fires every time the selection changes. If you click Feature A then Feature B, it fires twice. The selection is replaced each time
  • Record selection toggle (via Ctrl+click or Shift+click) — adds to the current selection. Click Feature A, then Ctrl+click Feature B, and now both are selected

For most apps, you want "Record selection changes" — single-click, single-selection. Multi-select is a special use case for things like batch operations.

List widget
User clicks item
Selection message
{objectId, fields...}
Map widget
Highlights + zooms
Try it

Build a two-way selection sync: Map click updates the List, and List click updates the Map. Configure a "Record selection changes" action on the Map targeting the List, then a "Record selection changes" action on the List targeting the Map. Now clicking either widget syncs the other. This is the most common action pattern in production EB apps.

Watch out

Both widgets must share the same data source (or at least use layers from the same web map). If your Map uses "Web Map Layer A" and your List uses a separately-added Feature Layer that happens to have the same URL, EB treats them as different data sources. The selection message won't match. Always connect widgets to the same data source reference in your app.

Lesson 3

Filter actions

Filtering is the second most important action type. Instead of selecting a single record, a filter action narrows the entire dataset shown in a target widget. Click a "Show only open shelters" button and the Map shows only open shelters. Pick a county from a dropdown and the Table shows only records from that county.

Set filter

Apply a filter

Applies a SQL-like where clause to the target widget's data. Records that don't match are hidden. You define the filter expression when configuring the action.

Reset

Reset filter

Clears any active filter and shows all records again. Always pair a "Set filter" with a "Reset filter" button — users need an escape hatch.

There are two common patterns for triggering filters:

Pattern 1: Button-driven filters. You have specific buttons like "Show Active," "Show Closed," "Show All." Each button has a click trigger wired to a "Set filter" or "Reset filter" action.

1
Select a Button widget and go to its Action tab
2
Under the "Button clicks" trigger, click + Add action
3
Choose Message action and select the target widget (e.g., a Map or Table)
4
In the action settings, choose "Filter data records"
5
Define the filter expression. For example: Status = 'Open'
6
Create a second Button labeled "Show All" with a "Reset filter" action targeting the same widget
Button: "Active Only"
Button clicks
Set filter
Status = 'Open'
Map widget
Shows only open

Pattern 2: Filter widget. The Filter widget provides a built-in UI — dropdowns, checkboxes, sliders — that filter connected widgets automatically. This is simpler to set up but gives you less control over the UI.

Filter widget vs. action filters

The Filter widget is a pre-built UI element that creates filters for you — you don't configure actions at all. Just drop it in, connect it to a data source, and it filters any widget using that data source. Action-based filters give you more control: custom buttons, specific SQL expressions, conditional logic. Use the Filter widget when you want "standard filter UI." Use action-based filters when you need custom behavior.

Common pitfall

Filters don't stack by default. If you have a "Status = Open" button and a "County = Miami" button, clicking both doesn't give you "Open shelters in Miami." Each Set filter action replaces the previous filter. To combine filters, you need to use a single filter expression with multiple conditions, or use the Filter widget which handles combining natively.

Quick check: What's the difference between a selection and a filter?

Lesson 4

Zoom to and Pan to actions

One of the most satisfying actions to wire up. A user clicks an item in a List, and the Map flies over to that feature's location. It makes your app feel alive — like the widgets are actually working together.

There are two flavors:

ActionBehaviorBest for
Zoom toFlies the map to the feature and adjusts the zoom level to fit the feature's extentPolygon features (counties, parcels). Shows the full shape
Pan toCenters the map on the feature without changing the current zoom levelPoint features (shelters, incidents). Keeps your current zoom context
1
Select the List widget (the trigger source) and go to its Action tab
2
Under "Record selection changes", click + Add action
3
Choose Message action, target your Map widget
4
In the action settings, choose "Zoom to" (or "Pan to" for points)
5
Optionally set a zoom scale — this is how close you want the map to zoom. For cities, try 1:100,000. For buildings, try 1:5,000
List widget
Item clicked
Zoom to action
Map widget
Flies to feature
Try it

Wire up a List → Zoom to → Map action, then switch to Live View and click items in the List. Watch the map animate. Now try adding a second action on the same trigger: List → Select record → Map. This way, clicking a List item both zooms the map AND highlights the feature. Two actions from one trigger — you'll learn more about multi-action chains in Lesson 7.

Key concept: zoom scale matters

If you don't set a zoom scale, EB will zoom to fit the feature's geometry. For points, this often zooms way too close (street-level). For polygons, it usually works well. For point layers, always set a zoom scale — something like 1:50,000 to 1:200,000 depending on your use case. You can also use "Zoom to" without a scale and it'll use the default, but test it before publishing.

Watch out

"Zoom to" only works if the trigger message includes geometry. If you're triggering from a Table widget that's connected to a tabular data source (no geometry), the Zoom to action silently fails. The Map can't zoom to something that has no location. Make sure your data source has geometry, or use a join to a spatial layer.

Lesson 5

Open and Close widget actions

Not every widget needs to be visible all the time. A details panel should appear when something is selected. A filter panel should slide in when a button is clicked. An info box should pop open on demand. Open/Close actions control widget visibility — show it, hide it, or toggle it.

Open

Open widget

Makes a hidden widget visible. If it's a Sidebar, it slides open. If it's a regular widget, it appears.

Close

Close widget

Hides a visible widget. The Sidebar slides closed. A regular widget disappears from the layout.

Toggle

Toggle

If it's open, close it. If it's closed, open it. Perfect for a single button that shows/hides a panel.

The Sidebar widget is the most common target for Open/Close actions because it's specifically designed to slide in and out. But you can also show/hide Sections, Windows, and other container widgets.

1
Add a Sidebar widget to your page. Put whatever content you want inside it (Text widgets, Charts, details panels)
2
Add a Button widget to your main layout area. Label it "Details" or "Info Panel" or whatever fits
3
Select the Button, go to its Action tab
4
Under "Button clicks", add a Framework action (not Message action)
5
Select "Open widget" and choose your Sidebar widget as the target
6
Test in Live View — click the button, Sidebar slides open
Framework actions vs. Message actions

Open/Close is a framework action, not a message action. The difference:

  • Message actions send data between widgets (selection, filter, zoom)
  • Framework actions control the app itself (open/close widgets, scroll to sections, navigate pages)

When adding an action, you choose between these two categories. If you're looking for "Open widget" and can't find it, you're probably looking in Message actions instead of Framework actions.

Button
Button clicks
Framework action
Open widget
Sidebar
Slides open
Try it

Set up a complete show/hide pattern: Button A opens the Sidebar, and a close button inside the Sidebar closes it. Or use a single Button with a Toggle action — one click opens, next click closes. The Toggle approach is cleaner for most UIs and requires only one button.

Common mistake

You're looking for "Open widget" in Message actions. It's not there. Open/Close/Toggle are Framework actions. When you click "+ Add action" on a trigger, you'll see both "Message action" and "Framework action" as categories. Click "Framework action" to find visibility controls. This trips up almost everyone the first time.

Lesson 6

Flash and Select actions

When working with maps, you often want to draw attention to a specific feature without fully zooming to it. EB gives you three tools for this, and knowing when to use each one is important.

ActionWhat it doesWhen to use
FlashBriefly blinks the feature on the map (flashes it 2-3 times with a highlight color, then stops)Drawing momentary attention — "look here" — without changing the map state. Great for hover effects or quick identification
SelectHighlights the feature with a persistent selection symbol. Stays highlighted until something else is selectedIdentifying the "active" or "current" feature. Standard click-to-select behavior
Zoom toFlies the map to the feature's location and adjusts the extentNavigating to a feature. Changes the map view. Use when the feature might be off-screen

You can combine these. A common pattern: List item click triggers Select + Zoom to. The feature gets highlighted AND the map flies to it. A Table row hover triggers Flash only — a quick "here it is" without moving the map.

Table row hover
Quick peek
Flash
Blink on map
List item click
Full navigation
Select + Zoom to
Highlight + fly
Think of it this way

Imagine a classroom. Flash is like pointing at a student — everyone looks for a second, then attention returns to normal. Select is like calling a student to the front of the room — they stay there, highlighted. Zoom to is like walking over to their desk — you physically move to where they are.

Pro tip: Flash is underused

Most people skip Flash and go straight to Zoom to. But Flash is excellent for preview behavior — hover over a table row and the feature blinks on the map without moving it. Users keep their current map view (which they might have carefully set up) while still seeing which feature corresponds to which row. It's a power-user pattern that makes your app feel professional.

Quick check: You want users to hover over list items and see the corresponding feature highlighted briefly on the map, without the map moving. Which action?

Complex Action Chains

You can wire single actions. Now let's build sequences, combine message types, use URL parameters, and turn the Sidebar into a command center.

Lesson 7

Multi-action sequences

Here's where EB actions get powerful: one trigger, multiple actions. A single click on a List item can zoom the map, open a sidebar, update a text panel, and filter a chart — all at once.

To add multiple actions to the same trigger, just click + Add action again under the same trigger event. You can add as many as you need.

List item click
Zoom to (Map)
Select record (Map)
Open widget (Sidebar)
Message action (Text)

All four actions fire from the same "Record selection changes" trigger on the List widget. The user clicks once. Four things happen.

Execution order

Actions execute in the order they appear in the Action tab, from top to bottom. EB processes them sequentially, though the execution is fast enough that they appear simultaneous to the user. However, if one action depends on another (e.g., you want to filter first, then zoom to the filtered results), order matters. Drag actions to reorder them in the Action tab.

When actions conflict

Be careful with actions that target the same widget with contradictory instructions. If you wire two filter actions on the same trigger — one saying Status = 'Open' and another saying Status = 'Closed' — the last one wins. The first filter applies, then gets immediately overwritten by the second. This seems obvious, but it's easy to accidentally create conflicts when you have many actions, especially if you forget about actions you configured earlier.

Try it: The "full details" pattern

This is the most common production pattern. Set up a List widget with these actions on "Record selection changes":

  • Message action → Map (zoom to the selected feature)
  • Message action → Map (select the feature, so it's highlighted)
  • Framework action → Sidebar (open the details panel)
  • Message action → Text widget in Sidebar (pass the selection data so the Text widget can display it)

Now when a user clicks a List item: the map zooms and highlights, the sidebar slides open, and the details appear. One click, full context. This is what professional EB apps feel like.

Quick check: You have 3 actions on the same trigger. Which one executes first?

Lesson 8

Message actions vs. data actions

This is one of the most misunderstood concepts in EB. There are two completely different ways widgets can communicate, and confusing them causes most "why doesn't my action work?" problems.

Message Actions

Explicit wiring

You manually configure them in the Action tab. You choose the trigger, the action type, and the target widget. Nothing happens unless you wire it up. Full control.

Data Actions

Implicit through shared data

Widgets that share the same data source automatically sync. If a Map and Table use the same data source, filtering one filters the other. No action configuration needed.

Data actions happen automatically because of how EB's data framework works. When two widgets share a data source, they share the same underlying data view. Apply a filter to one — the data source is filtered — the other widget sees the filtered data. No wiring required.

Message actions are the ones you configure in the Action tab. They're explicit instructions: "When this trigger fires, send this message to that widget."

AspectMessage ActionsData Actions (shared data source)
SetupManual — Action tab configurationAutomatic — just use the same data source
FilteringExplicit: "Set filter on Widget B"Implicit: filter the data source, both widgets update
SelectionExplicit: "Select this record in Widget B"Implicit: select in one, selected in all (if enabled)
ControlFine-grained — choose exactly what happensAll or nothing — all connected widgets are affected
When to useCross-data-source communication, specific behaviorsWhen you want all widgets to stay in sync automatically
The golden rule

Same data source = automatic sync. Different data sources = you need message actions. If your Map and Table both point to the same data source, filtering either one filters both. If they use different data sources (even if the underlying layer is the same), you need explicit action wiring for them to communicate.

The "double filter" trap

If two widgets share a data source (automatic sync) AND you also configure a message action between them (explicit wiring), you can get double-firing. The filter applies once through the data source link, and a second time through the message action. This can cause confusing behavior — flickering, double-counting, or unexpected results. Pick one approach: shared data source OR message actions. Not both for the same interaction.

Think of it this way

Data source sync is like two TVs in the same room showing the same channel — change the channel, both TVs update because they're on the same cable box. Message actions are like having a remote control that you point at a specific TV — you decide which one gets the instruction, and when.

Lesson 9

URL parameter actions

URL parameters are hidden superpowers. They let you deep link into specific states of your app — a particular filter, a selected feature, a specific page. Share a URL and the recipient sees exactly what you see.

EB supports URL parameters in two directions:

Read

On app load

Your app reads URL parameters when it first loads and uses them to set initial state — apply a filter, select a record, navigate to a page.

Write

From actions

An action updates the browser URL with current state — so if a user bookmarks or shares the link, it preserves their selections and filters.

The most common use case: filtered deep links. You send someone a URL like:

URL with parameters
https://experience.arcgis.com/experience/abc123/?region=Southeast&status=Open

When the app loads, it reads region=Southeast and status=Open and automatically applies those filters. The user lands on a pre-filtered view without clicking anything.

1
In EB, go to App Settings (gear icon in the top toolbar)
2
Find the URL Parameters section
3
Add a new parameter. Give it a name (e.g., region) and connect it to a data source field
4
EB will automatically apply a filter when the parameter is present in the URL. Any widget connected to that data source will filter accordingly
URL parameters work with shared data sources

When a URL parameter applies a filter to a data source, ALL widgets connected to that data source are filtered. This is the data action pattern from Lesson 8 — the data source is the common link. A single URL parameter can filter your Map, Table, List, and Chart simultaneously.

Try it

Set up a URL parameter called status linked to a Status field in your data. Publish your app. Then manually add ?status=Open to the URL. Your app should load with only "Open" records visible. Now try ?status=Closed. Different filtered view, same app. This is how you build shareable dashboards.

Watch out

URL parameter values are case-sensitive. If your field value is "Open" (capital O), the URL parameter must be ?status=Open — not ?status=open. Also, special characters in values need URL encoding. Spaces become %20, ampersands become %26. Test your deep links with the exact values from your data.

Lesson 10

Conditional visibility with expressions

Sometimes you want an action to fire only under certain conditions. Show the "Details" panel only if a feature is selected. Display a warning only if a value exceeds a threshold. Hide a section when there's no data to show.

EB supports conditional visibility on widgets — you can set a widget to be visible or hidden based on an expression. This isn't technically an "action" (it's a widget property), but it works hand-in-hand with the action system to create smart, responsive layouts.

1
Select the widget you want to conditionally show/hide
2
In the General tab of the settings panel, find the "Visible" toggle
3
Click the data expression icon next to the toggle (it looks like a small database icon or curly brace)
4
Write an expression that returns true (visible) or false (hidden)

Common conditional visibility patterns:

GoalExpression logic
Show only when a record is selectedCheck if the selection count > 0
Show only when a field has a specific valueCheck if Status = 'Open'
Hide when there's no dataCheck if the record count > 0
Show based on user roleCheck the portal user's role or group membership
Visibility vs. Open/Close actions

You can hide widgets two ways: conditional visibility (expression-based, automatic) or Open/Close actions (trigger-based, manual). Use conditional visibility when the show/hide logic depends on data state — "show this panel when something is selected." Use Open/Close actions when the show/hide is user-initiated — "show this panel when the user clicks a button."

The empty-state trap

If you use conditional visibility to hide a widget when no record is selected, test what happens on initial load. When the app first opens, nothing is selected, so the widget starts hidden. That's usually what you want. But if the widget contains important instructions or context, users might not know it exists. Consider showing a placeholder message instead of hiding entirely.

Try it

Create a "Details Panel" Section widget. Set its visibility expression to check whether the current selection count is greater than zero. Inside the Section, put Text widgets that display selected record fields. When no feature is selected, the entire panel disappears. When a feature is clicked, the panel appears with details. Clean, professional, and no Open/Close actions needed.

Lesson 11

The Sidebar widget as action hub

The Sidebar widget is the secret weapon of EB action architecture. It's not just a panel that slides in and out — it can have multiple views (controller panels), and you can switch between them using actions. This turns the Sidebar into a central command panel for your entire app.

Think of it this way

The Sidebar is like a TV with multiple channels. Each "view" in the Sidebar is a channel — one shows shelter details, another shows damage reports, another shows contact info. Actions act as the remote control, switching between channels based on what the user does elsewhere in the app.

The Sidebar widget has a unique feature: multiple panels (views). You can create several different layouts inside a single Sidebar, and use actions to switch between them.

1
Add a Sidebar widget. In its settings, you'll see a Views section — it starts with one view
2
Click + Add view to create additional views. Each view is a separate layout — add different widgets to each one
3
Name each view clearly: "Shelter Details," "Damage Report," "Contacts"
4
Now configure actions from other widgets. On a "Shelter" List item click, add a Framework action: Open widget → Sidebar → View: Shelter Details
5
On a "Damage" Table row click, add a Framework action: Open widget → Sidebar → View: Damage Report
Shelter List click
Open Sidebar
View: Shelter Details
Damage Table click
Open Sidebar
View: Damage Report

The same Sidebar widget, but showing completely different content depending on which widget triggered it. This is the controller pattern — the Sidebar is the centralized display hub, and other widgets around the app control what it shows.

The controller pattern

Instead of having multiple hidden panels all over your layout, use one Sidebar with multiple views. All "detail" content goes through the Sidebar. This keeps your layout clean, your action wiring centralized, and your app predictable. Users always know where to look for details — it's always in the Sidebar.

Sidebar sizing

The Sidebar has a fixed width that you set in its properties. Make sure it's wide enough for your most detailed view. If one view has a wide Table and another has a narrow Text widget, size the Sidebar for the Table. The narrow content will just have extra space — better than a cramped Table with horizontal scrolling.

Quick check: What makes the Sidebar widget special compared to regular container widgets?

Lesson 12

Cross-page navigation actions

EB apps can have multiple pages. When a user clicks something, you can navigate them to a different page — with data context intact. This is how you build multi-page applications that feel like a single cohesive experience.

Multi-page

Page navigation

Use the "Navigate to page" framework action. Click a button on Page 1, land on Page 2. Common for overview → detail page flows.

Single-page

Section visibility

Instead of separate pages, use one page with Sections that show/hide. Faster transitions, simpler data sharing. Good for apps with 2-3 "views."

When to use multi-page: When your views are truly different — a dashboard page, a data entry page, a settings page. Each page has its own layout, and users don't need to see them simultaneously.

When to use single-page with visibility: When your views share data context — an overview map that transitions to a detail view. Single-page keeps the data source state (filters, selections) intact. Page navigation can reset state if not handled carefully.

1
Create multiple pages in your EB app (Page panel on the left → + Add page)
2
On the triggering widget (e.g., a Button on Page 1), go to the Action tab
3
Add a Framework action → Navigate to page
4
Select the target page from the dropdown
5
If you need to pass data to the target page, use URL parameters (Lesson 9). The navigate action can append parameters to the URL
Passing data between pages

This is the hard part of multi-page apps. When you navigate from Page 1 to Page 2, the selection state from Page 1 does not automatically carry over. Page 2 loads fresh. To pass context, use URL parameters: encode the selected feature ID (or filter values) into the URL when navigating, then read them on Page 2 to restore state. This is extra work, but it's the only reliable way.

The page navigation state trap

A common complaint: "I had a filter set on Page 1, navigated to Page 2, came back to Page 1, and my filter was gone." By default, EB resets page state on navigation. If preserving state across page visits matters, consider the single-page approach with Section visibility instead. Or use URL parameters to encode state, as described above.

Button (Page 1)
"View Details"
Navigate to page
Page 2 + ?id=42
Page 2 loads
Reads id=42, filters
Try it

Build a simple two-page app. Page 1: a List of features with a "View Details" button in each list item. Page 2: a detail layout with Text widgets showing fields. Wire the List item's button click to navigate to Page 2. For the advanced version, pass the selected feature's ObjectID as a URL parameter and use it to filter Page 2's data source. This is the overview → detail pattern that scales to any data type.

Production Action Patterns

Architecture, debugging, performance, and real-world workflow patterns. This is how you build apps that hold up under operational pressure.

Lesson 13

Action architecture for complex apps

When your app has 15+ widgets and dozens of actions, the wiring becomes hard to reason about. You click a button and something unexpected happens — or nothing happens at all. The problem isn't the actions themselves; it's the lack of a plan.

Before building a complex app, diagram your action flows. On paper, in a whiteboard tool, or in a text document. List every trigger → action chain. This saves hours of debugging later.

The Hub and Spoke pattern

The most maintainable action architecture is hub and spoke. Choose one or two "hub" widgets that receive all actions — typically a Map and a Sidebar. All other widgets (Lists, Tables, Buttons) are "spokes" that trigger actions pointing at the hubs. This means actions flow in one direction: from spokes to hubs. Never from hub to hub, never from spoke to spoke.

List widget
Spoke
Map + Sidebar
Hub
Table widget
Spoke
Filter buttons
Spoke
Map + Sidebar
Hub
Search widget
Spoke

Why this works: when something goes wrong, you know the action came from a spoke and the problem is either in the spoke's trigger or the hub's handling. You never have to trace through a chain of six widgets to find where the signal got lost.

Avoid spaghetti wiring

The antipattern: Widget A triggers Widget B, which triggers Widget C, which triggers Widget D, which triggers Widget A. Circular chains. EB doesn't prevent you from creating these, but they cause unpredictable behavior — infinite loops, race conditions, or actions that seem to fire twice. If you find yourself creating a chain longer than two steps (trigger → action → reaction), restructure to hub and spoke.

ArchitectureSymptomFix
SpaghettiChanging one action breaks three others. Can't remember what triggers whatRefactor to hub and spoke
Duplicate wiringSame behavior configured in both message actions AND shared data sourcePick one method. Delete the other
Invisible chainsAction triggers data source sync which triggers another widget's conditional visibility which...Document implicit behaviors. Minimize shared data sources across independent widget groups
Try it: action diagram

Open your most complex EB app. In a text editor, list every widget and its configured actions (check each widget's Action tab). Draw arrows showing trigger → target. You will almost certainly find: (1) actions you forgot about, (2) duplicate wiring, (3) widgets with no actions that should have them. This exercise alone will improve your app.

Lesson 14

Custom URL schemes for app state

Building on Lesson 9's URL parameters, let's go further. In a production app, you often need to encode full app state into the URL — not just one filter, but the current page, multiple filters, a selected feature, and a sidebar view. This is how you build truly shareable, bookmarkable application states.

Why encode state in URLs?
  • Shareability — email a colleague a link that opens exactly what you see
  • Bookmarkability — save filtered views as browser bookmarks
  • Repeatability — recreate a specific app state for training or documentation
  • Integration — other apps or dashboards can link directly to specific states in your EB app

Design your URL parameter scheme upfront, like an API:

URL Parameter Scheme
// Base URL
https://experience.arcgis.com/experience/abc123/

// Filters (one parameter per filterable field)
?region=Southeast
&status=Open
&type=Shelter

// Selection (feature ID for direct selection)
&selected=4521

// Page (for multi-page apps)
&page=details

// View options
&sidebar=open
&view=table
1
Plan your parameters. Decide which pieces of state matter for sharing. Not everything needs a URL parameter — just the state that defines a "view"
2
Register parameters in EB. In App Settings → URL Parameters, define each one and link it to a data source field or app behavior
3
Test on load. Manually construct URLs with different parameter combinations and verify the app loads into the correct state
4
Document the scheme. If other systems will link to your app, publish the URL parameter spec so they know what's available
Multiple filter parameters

You can define multiple URL parameters, each linked to a different field. When multiple parameters are present, EB applies them as an AND filter: ?region=Southeast&status=Open means "Southeast AND Open." This is the combined filtering behavior you can't easily get from button-based filter actions (see Lesson 3's warning about filters not stacking).

URL length limits

Browsers typically support URLs up to 2,000-8,000 characters. If you're encoding many parameters with long values, you can hit this limit. Keep parameter names short (r instead of region) and values compact. For very complex state, consider encoding to a hash and using a lookup table — but that's beyond EB's built-in capabilities.

Try it

Take an existing app and add three URL parameters: one for a geographic filter (region/county), one for a status filter, and one for feature selection. Publish the app. Then generate three different URLs with different parameter values and verify each one loads into the correct state. Share them with a colleague — they should see exactly what you intended.

Lesson 15

Debugging actions that don't fire

The single most frustrating experience in EB: you configured an action, you're sure the wiring is right, but nothing happens. Or it works in the builder's Live View but not in the published app. Or it worked yesterday and stopped today.

Here is the systematic debugging checklist. Work through it top to bottom. The problem is almost always one of these.

1
Is the action on the right widget? Actions live on the trigger widget, not the target. The most common mistake is configuring the action on the widget you want to respond, instead of the widget that fires the trigger
2
Is it the right trigger event? "Record selection changes" and "Record created" are different. "Button clicks" only fires on Button widgets — not clickable List items. Open the Action tab on the trigger widget and verify the correct event
3
Do both widgets share a compatible data source? If the trigger widget uses Data Source A and the target expects Data Source B, the message can't be matched. Even if both data sources point to the same feature layer URL, EB treats them as different sources if they were added separately
4
Is the target widget visible? If a target widget is hidden by conditional visibility (Lesson 10) or inside a collapsed Section, some actions won't fire. EB may skip actions targeting widgets that aren't currently rendered
5
Did you test in Live View AND the published app? The builder's Live View simulates the published app but doesn't perfectly replicate it. Some actions (especially URL parameters and page navigation) behave differently. Always test both
6
Is there a conflicting action? If two actions target the same widget with contradictory instructions (Lesson 7), the last one wins and the first appears to not work
7
Is the data source loaded? If the data source hasn't finished loading when the action fires (common on slow connections), the action may silently fail. This is especially common with "on app load" triggers
8
Clear browser cache and hard reload. EB caches aggressively. A published app can serve stale configs for minutes. Ctrl+Shift+R (hard reload) or open in incognito to test with a clean cache
The #1 silent failure: data source mismatch

This accounts for easily half of all "action doesn't work" reports. The Map uses "Web Map → Layer 1" as its data source. You add a separate reference to the same feature layer URL for the List widget. EB sees two different data source objects, even though they point to the same data. Selection messages from one can't be received by the other. The fix: always connect widgets to the same data source reference. In the data panel, you should see one data source entry used by multiple widgets — not two separate entries with the same URL.

"It works in builder but not published"

The most maddening bug class. Common causes:

  • Draft vs. published config mismatch. You saved changes in the builder but didn't re-publish. The published app is running the old config. Always re-publish after changes
  • URL parameter differences. The builder preview URL has different parameters than the published URL
  • Authentication. The published app may not have the same permissions as your builder session. Data sources that load fine for you might fail for anonymous users
  • Caching. The published app caches configs. Clear cache and wait a few minutes after publishing

Quick check: Your List → Map zoom action works in Live View but not in the published app. What's the FIRST thing to check?

Lesson 16

Real-world patterns: operational workflows

Let's move beyond theory and look at complete action architectures for real production apps. These patterns come from disaster response and operational applications — where the app has to work correctly under pressure.

Pattern 1: Shelter Status Dashboard

An emergency management team needs to see shelter locations, current status, and capacity. They need to filter by status, click a shelter to see details, and update information.

Filter buttons
Open / Closed / All
Map + List
Shared data source filters
List item click
Map: zoom + select
Sidebar: open details view
Map feature click
List: select + scroll
Sidebar: open details view
WidgetRoleActions configured
Filter buttons (3x Button)Spoke — triggers"Button clicks" → Set filter on shared data source (Open/Closed/Reset)
ListSpoke — triggers"Record selection changes" → Map (zoom to + select) + Sidebar (open, details view)
MapHub — receives and triggers"Record selection changes" → List (select) + Sidebar (open, details view)
SidebarHub — receivesReceives open/close + selection messages. Multiple views: Details, Edit, Stats

Pattern 2: Damage Assessment Workflow

Field teams submit damage reports. The operations center needs to review them, filter by severity, and drill into individual reports for detail and action.

Severity filter
Dropdown
Table + Map
Show matching reports
Table row click
Map: zoom to
Sidebar: report details + photos
"Export" button
Navigate to Export page
with current filters in URL
The pattern behind the patterns

Both workflows follow the same structure: Filter → Browse → Select → Detail. Users narrow the data (filter), scan results (browse in List/Table), pick one (select), and see the full picture (detail in Sidebar). This is the universal operational app flow. If your app follows this structure, users already know how to use it — even if they've never seen your specific app before.

Pattern 3: Multi-step edit workflow

Some apps need more than read-only viewing. Users select a feature, then edit its attributes. This requires an action chain that transitions from "view" to "edit" mode.

1
User selects a feature (Map or List click) → Sidebar opens in View mode (read-only details)
2
User clicks "Edit" button inside the Sidebar → Sidebar switches to Edit view (with an Edit widget or Smart Editor)
3
User submits the edit → The data source refreshes → Map and List update to show the new values
4
Sidebar returns to View mode showing the updated details
Edit workflows are fragile

The edit → refresh → redisplay cycle involves multiple async operations. The data source needs to re-query the server after an edit, which takes time. If other actions fire before the refresh completes, they'll operate on stale data. Build in a manual refresh button as a fallback. Don't rely solely on automatic data source refresh after edits — sometimes it doesn't trigger reliably.

Try it

Pick one of the three patterns above and build it. Start with Pattern 1 (Shelter Status Dashboard) — it's the most common and teaches the full range of action types. Use any point feature layer (even a public Esri sample). Focus on getting the action wiring right before worrying about visual design. A working ugly app beats a beautiful broken one every time.

Lesson 17

Performance and action limits

EB doesn't impose a hard limit on the number of actions you can configure. But that doesn't mean "add as many as you want." Every action adds processing overhead. At some point, your app starts feeling sluggish — clicks take a beat too long, the map stutters during zoom, the sidebar lags when opening.

Practical limits
  • 10-15 actions per widget is comfortable. Beyond that, audit for redundancy
  • 30-50 total actions in an app is a healthy range. Beyond that, consider simplifying
  • Chained actions (A triggers B triggers C) compound latency. Each hop adds processing time. Keep chains to 2 hops maximum
  • Actions targeting heavy widgets (Maps, Charts) cost more than actions targeting light widgets (Text, Image)

How actions execute: EB processes actions sequentially within a trigger, but the effects can be asynchronous. When you trigger "Zoom to" on a Map, EB sends the instruction and moves to the next action without waiting for the map to finish animating. This is usually fine, but it means action effects can overlap in unexpected ways.

IssueCauseSolution
Map stutters during zoomMultiple zoom/pan actions firing simultaneouslyConsolidate to a single zoom action. Remove redundant pan-then-zoom chains
Filter applies then immediately resetsTwo conflicting filter actions, or a shared data source resetting the filterAudit all filter actions on that trigger. Check for data source sync conflicts (Lesson 8)
Sidebar opens with stale contentSidebar opens before the data message arrivesReorder actions: put the data message action before the "Open widget" action
App feels slow after adding many actionsToo many actions firing on high-frequency triggersMove expensive actions off of "extent changes" (fires constantly during pan). Use "Record selection changes" instead, which fires once per click
The "extent changes" performance trap

The Map widget has a trigger called "Extent changes" — it fires every time the map view moves. During a pan, this fires dozens of times per second. If you attach heavy actions to this trigger (filter a large dataset, update multiple widgets), your app will grind to a halt. Only use "Extent changes" for lightweight operations, like updating a coordinate display. For everything else, use "Record selection changes" or "Button clicks."

Race conditions

A race condition happens when two actions compete to modify the same state and the result depends on which one finishes first. For example: Action A sets a filter, Action B resets the filter. If they fire on the same trigger, the final state depends on execution order. EB processes them top-to-bottom, but async effects (data loading, map rendering) can cause the visual result to differ from what you expect. If you see flickering or inconsistent state, check for actions that compete over the same widget.

DO

Keep it lean

One trigger, 2-4 actions. Hub and spoke architecture. Avoid long chains. Test on mobile.

DON'T

Wire everything to everything

Resist the urge to make every widget react to every event. Users don't need that. It slows the app and makes debugging impossible.

Quick check: Which Map trigger is dangerous to attach heavy actions to, and why?

Reference

Further reading and resources