How widgets talk to each other. From your first click-to-zoom action to multi-step workflows that power real operational apps.
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.
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."
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.
Something happens. A user clicks a feature, selects a row, clicks a button, or the page loads.
The trigger creates a message containing data — which feature was clicked, which record was selected.
A target widget receives the message and does something — zooms, filters, shows content, opens a panel.
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.
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.
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.
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.
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.
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.
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.
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.
| Widget | Can trigger selection? | Can receive selection? |
|---|---|---|
| Map | Yes — click/tap a feature | Yes — highlights the selected feature |
| List | Yes — click/tap a list item | Yes — scrolls to and highlights the item |
| Table | Yes — click a table row | Yes — highlights the selected row |
| Chart | Yes — click a bar/slice/point | Yes — highlights the segment |
| Text | No | Yes — displays selected record data |
| Image | No | Yes — shows image for selected record |
| Button | No (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.
The Map widget has two selection-related triggers:
For most apps, you want "Record selection changes" — single-click, single-selection. Multi-select is a special use case for things like batch operations.
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.
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.
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.
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.
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.
Status = '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.
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.
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.
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:
| Action | Behavior | Best for |
|---|---|---|
| Zoom to | Flies the map to the feature and adjusts the zoom level to fit the feature's extent | Polygon features (counties, parcels). Shows the full shape |
| Pan to | Centers the map on the feature without changing the current zoom level | Point features (shelters, incidents). Keeps your current zoom context |
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.
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.
"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.
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.
Makes a hidden widget visible. If it's a Sidebar, it slides open. If it's a regular widget, it appears.
Hides a visible widget. The Sidebar slides closed. A regular widget disappears from the layout.
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.
Open/Close is a framework action, not a message action. The difference:
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.
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.
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.
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.
| Action | What it does | When to use |
|---|---|---|
| Flash | Briefly 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 |
| Select | Highlights the feature with a persistent selection symbol. Stays highlighted until something else is selected | Identifying the "active" or "current" feature. Standard click-to-select behavior |
| Zoom to | Flies the map to the feature's location and adjusts the extent | Navigating 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.
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.
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.
You can wire single actions. Now let's build sequences, combine message types, use URL parameters, and turn the Sidebar into a command center.
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.
All four actions fire from the same "Record selection changes" trigger on the List widget. The user clicks once. Four things happen.
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.
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.
This is the most common production pattern. Set up a List widget with these actions on "Record selection changes":
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.
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.
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.
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."
| Aspect | Message Actions | Data Actions (shared data source) |
|---|---|---|
| Setup | Manual — Action tab configuration | Automatic — just use the same data source |
| Filtering | Explicit: "Set filter on Widget B" | Implicit: filter the data source, both widgets update |
| Selection | Explicit: "Select this record in Widget B" | Implicit: select in one, selected in all (if enabled) |
| Control | Fine-grained — choose exactly what happens | All or nothing — all connected widgets are affected |
| When to use | Cross-data-source communication, specific behaviors | When you want all widgets to stay in sync automatically |
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.
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.
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.
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:
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.
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:
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.
region) and connect it to a data source fieldWhen 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.
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.
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.
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.
true (visible) or false (hidden)Common conditional visibility patterns:
| Goal | Expression logic |
|---|---|
| Show only when a record is selected | Check if the selection count > 0 |
| Show only when a field has a specific value | Check if Status = 'Open' |
| Hide when there's no data | Check if the record count > 0 |
| Show based on user role | Check the portal user's role or group membership |
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."
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.
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.
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.
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.
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.
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.
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.
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.
Use the "Navigate to page" framework action. Click a button on Page 1, land on Page 2. Common for overview → detail page flows.
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.
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.
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.
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.
Architecture, debugging, performance, and real-world workflow patterns. This is how you build apps that hold up under operational pressure.
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 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.
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.
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.
| Architecture | Symptom | Fix |
|---|---|---|
| Spaghetti | Changing one action breaks three others. Can't remember what triggers what | Refactor to hub and spoke |
| Duplicate wiring | Same behavior configured in both message actions AND shared data source | Pick one method. Delete the other |
| Invisible chains | Action triggers data source sync which triggers another widget's conditional visibility which... | Document implicit behaviors. Minimize shared data sources across independent widget groups |
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.
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.
Design your URL parameter scheme upfront, like an API:
// 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
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).
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.
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.
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.
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.
The most maddening bug class. Common causes:
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.
| Widget | Role | Actions configured |
|---|---|---|
| Filter buttons (3x Button) | Spoke — triggers | "Button clicks" → Set filter on shared data source (Open/Closed/Reset) |
| List | Spoke — triggers | "Record selection changes" → Map (zoom to + select) + Sidebar (open, details view) |
| Map | Hub — receives and triggers | "Record selection changes" → List (select) + Sidebar (open, details view) |
| Sidebar | Hub — receives | Receives 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.
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.
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.
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.
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.
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.
| Issue | Cause | Solution |
|---|---|---|
| Map stutters during zoom | Multiple zoom/pan actions firing simultaneously | Consolidate to a single zoom action. Remove redundant pan-then-zoom chains |
| Filter applies then immediately resets | Two conflicting filter actions, or a shared data source resetting the filter | Audit all filter actions on that trigger. Check for data source sync conflicts (Lesson 8) |
| Sidebar opens with stale content | Sidebar opens before the data message arrives | Reorder actions: put the data message action before the "Open widget" action |
| App feels slow after adding many actions | Too many actions firing on high-frequency triggers | Move expensive actions off of "extent changes" (fires constantly during pan). Use "Record selection changes" instead, which fires once per click |
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."
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.
One trigger, 2-4 actions. Hub and spoke architecture. Avoid long chains. Test on mobile.
Resist the urge to make every widget react to every event. Users don't need that. It slows the app and makes debugging impossible.