Introduction
This post documents an experimental workflow developed and tested in ArcGIS Online Experience Builder for conditional visual formatting inside the Table widget. The approach uses an Arcade Data source to generate a virtual FeatureSet containing calculated string fields. Those fields can contain Unicode symbols or HTML/CSS generated according to attribute values.
The resulting FeatureSet is then used as the source of a standard Experience Builder Table. In the tested environment this allowed Unicode indicators, conditionally colored text, colored numeric values, and an experimental colored block that visually approximates a conditionally formatted table cell. The original source data are not modified.
Note on Experience Builder advanced formatting. Experience Builder also supports Arcade-based dynamic content and styling in supported widget contexts. The workflow described here addresses a different case: conditional visual formatting of values displayed inside the standard Table widget by generating formatted content in an Arcade Data FeatureSet. It should not be interpreted as native cell-level conditional formatting provided by the Table widget.
1. Basic idea
Workflow:
Original table → Arcade Data → Virtual FeatureSet → Table widget
For a generic reproducible example, assume the source table contains OBJECTID, REACH_ID (a unique common key), and AREA (a numeric value). The demonstration rules are deliberately arbitrary: AREA = -99999 means No Data; AREA < 100 means LOW; AREA >= 100 means HIGH.
2. Create the Arcade Data source
In Experience Builder use Data → Add data → Arcade, then read the source table with FeatureSetByPortalItem().
var p = Portal("https://YOURPORTAL.maps.arcgis.com");
var fs = FeatureSetByPortalItem(
p,
"YOUR_ITEM_ID",
2,
["OBJECTID", "REACH_ID", "AREA"],
false
);
Replace YOURPORTAL, YOUR_ITEM_ID, and the table/layer ID with your own values. Geometry is not required for this table example, so includeGeometry is false.
3. Complete Arcade example
The following complete script keeps the original attributes and creates five experimental fields so that the results can be compared side by side.
// ============================================================
// 1. PORTAL CONNECTION
// ============================================================
var p = Portal("https://YOURPORTAL.maps.arcgis.com");
// ============================================================
// 2. READ THE ORIGINAL TABLE
// ============================================================
var fs = FeatureSetByPortalItem(
p,
"YOUR_ITEM_ID",
2,
["OBJECTID", "REACH_ID", "AREA"],
false
);
// ============================================================
// 3. DEFINE THE VIRTUAL FEATURESET
// ============================================================
var output = {
fields: [
{
name: "OBJECTID",
alias: "OBJECTID",
type: "esriFieldTypeOID"
},
{
name: "REACH_ID",
alias: "Reach ID",
type: "esriFieldTypeString",
length: 50
},
{
name: "AREA",
alias: "Area",
type: "esriFieldTypeDouble"
},
{
name: "TEST_UNICODE",
alias: "Unicode",
type: "esriFieldTypeString",
length: 50
},
{
name: "TEST_HTML",
alias: "HTML text",
type: "esriFieldTypeString",
length: 150
},
{
name: "TEST_VALUE",
alias: "Unicode + value",
type: "esriFieldTypeString",
length: 50
},
{
name: "TEST_HTML_VALUE",
alias: "HTML value",
type: "esriFieldTypeString",
length: 150
},
{
name: "TEST_BACKGROUND",
alias: "Background test",
type: "esriFieldTypeString",
length: 300
}
],
geometryType: "",
features: []
};
// ============================================================
// 4. LOOP THROUGH SOURCE RECORDS
// ============================================================
for (var f in fs) {
var reachID = f.REACH_ID;
var area = f.AREA;
// TEST 1 - Unicode indicator
var unicodeResult = "";
if (area == -99999) {
unicodeResult = "⚪ ND";
}
else if (area < 100) {
unicodeResult = "🔴 LOW";
}
else {
unicodeResult = "🟢 HIGH";
}
// TEST 2 - HTML colored text
var htmlResult = "";
if (area == -99999) {
htmlResult = "<span style='color:gray'>ND</span>";
}
else if (area < 100) {
htmlResult = "<span style='color:red'>LOW</span>";
}
else {
htmlResult = "<span style='color:green'>HIGH</span>";
}
// TEST 3 - Unicode indicator + original value
var valueResult = "";
if (area == -99999) {
valueResult = "⚪ ND";
}
else if (area < 100) {
valueResult = "🔴 " + Text(area, "#,##0.00");
}
else {
valueResult = "🟢 " + Text(area, "#,##0.00");
}
// TEST 4 - Original value colored with HTML
var htmlValue = "";
if (area == -99999) {
htmlValue = "<span style='color:gray'>ND</span>";
}
else if (area < 100) {
htmlValue =
"<span style='color:red'>" +
Text(area, "#,##0.00") +
"</span>";
}
else {
htmlValue =
"<span style='color:green'>" +
Text(area, "#,##0.00") +
"</span>";
}
// TEST 5 - Experimental simulated cell background
// This styles a block inside the field; it does NOT
// modify the native Table cell itself.
var backgroundResult = "";
if (area == -99999) {
backgroundResult =
"<div style='" +
"background-color:gray;" +
"color:white;" +
"width:100%;" +
"padding:6px;" +
"text-align:center;" +
"font-weight:bold" +
"'>ND</div>";
}
else if (area < 100) {
backgroundResult =
"<div style='" +
"background-color:red;" +
"color:white;" +
"width:100%;" +
"padding:6px;" +
"text-align:center;" +
"font-weight:bold" +
"'>" +
Text(area, "#,##0.00") +
"</div>";
}
else {
backgroundResult =
"<div style='" +
"background-color:green;" +
"color:white;" +
"width:100%;" +
"padding:6px;" +
"text-align:center;" +
"font-weight:bold" +
"'>" +
Text(area, "#,##0.00") +
"</div>";
}
// BUILD OUTPUT RECORD
Push(
output.features,
{
attributes: {
OBJECTID: f.OBJECTID,
REACH_ID: reachID,
AREA: area,
TEST_UNICODE: unicodeResult,
TEST_HTML: htmlResult,
TEST_VALUE: valueResult,
TEST_HTML_VALUE: htmlValue,
TEST_BACKGROUND: backgroundResult
}
}
);
}
// ============================================================
// 5. RETURN THE VIRTUAL FEATURESET
// ============================================================
return FeatureSet(
Text(output)
);
4. Use the Arcade Data source in the Table widget
Run and apply the expression, then configure a Table widget to use the Arcade Data source. The output should expose OBJECTID, REACH_ID, AREA, TEST_UNICODE, TEST_HTML, TEST_VALUE, TEST_HTML_VALUE, and TEST_BACKGROUND.
Practical observation: after adding fields to the Arcade expression, the Table may continue to use the previous schema. If a new field appears in the Arcade output but not in the Table, make the Table re-read/refresh the Arcade Data source.
5. Filter the virtual table from a map selection
Retain the same common key in the source feature layer and in the virtual FeatureSet. Configure Map → Action → Record selection changes → Filter data.
Setting | Value |
|---|
Trigger data | Feature layer |
Trigger field | REACH_ID |
Action data | Arcade Data |
Action field | REACH_ID |
Mode | Default |
With a 1:1 relationship, selecting a map feature filters the virtual Table to the corresponding record.
6. Results verified in the tested environment
- Unicode indicators: worked.
- Unicode indicator + numeric value: worked.
- HTML text color using <span>: worked.
- HTML-colored numeric values: worked.
- Background-color block using <div>: worked experimentally.
Figure 1. Conditional visual formatting verified in the Experience Builder Table: Unicode indicator, HTML-colored text, Unicode indicator with numeric value, HTML-colored numeric value, and experimental simulated cell background.
7. Important distinction and limitations
This should not be described as native conditional formatting of the Table widget. A more accurate description is conditional visual formatting of the content of a virtual Arcade field displayed by the Table widget.
The background test does not style the native table cell. It renders an HTML block inside the field content and uses width, padding, background-color, text alignment, and font weight to visually approximate a colored cell.
The <div>-based background behavior should be treated as experimental. It was verified in the tested ArcGIS Online environment, but it should not be assumed to be a stable supported API contract. HTML/CSS sanitization or rendering behavior may change between updates or between ArcGIS Online and ArcGIS Enterprise. Keep production HTML simple and retest after updates.
Feature-count limitation and observed behavior. Esri documentation states a maximum of 50,000 features when a FeatureSet is used to construct a feature collection. The virtual FeatureSets constructed in this post fall within the type of workflow for which that documented limit is relevant. However, additional tests in the same ArcGIS Online environment successfully used 50,001 and 100,000 generated records in the Table widget, including direct queries of OBJECTID 50001 and 100000. Section 9 documents this discrepancy in detail. The observed behavior is experimental and should not be treated as a supported replacement for Esri's documented limit.
8. Additional validation: 1:N filtering and virtual wide-to-long transformation
Two additional tests were performed to verify that Arcade Data can support a 1:N workflow driven by a map selection. Both tests used the same common reach identifier in the map layer and in the table or virtual FeatureSet.
Test 1 - Existing physical 1:N table. An Arcade Data source was created from an existing table already organized in long form, with multiple discharge-duration records for each reach. A Map action (Record selection changes → Filter data records) connected the reach identifier in the feature layer to the same identifier in the Arcade Data source. Selecting one reach filtered the Table to all matching records, not just a single record. This confirms that a virtual Arcade Data source can participate in a 1:N filtering workflow.
Figure 2. Test 1: selecting reach Arno_3629 filters the 1:N Arcade Data table to multiple discharge-duration records for the same reach.
Test 2 - Virtual wide-to-long transformation. The source table was instead read in its original wide structure, where discharge values for different durations are stored in separate fields. Arcade dynamically transformed each source record into multiple virtual records containing Reach ID, Discharge, Duration label, and numeric Duration. The resulting virtual 1:N FeatureSet was then connected to the same map-selection filter. Selecting the same reach returned the expected multiple records and the discharge values matched the corresponding physical long-form table, apart from display rounding.
Figure 3. Test 2: Arcade creates a virtual 1:N table from a wide source table and the result is successfully filtered by the selected reach.
These tests extend the original 1:1 validation. In the tested ArcGIS Online Experience Builder environment, Arcade Data was able both to expose an existing 1:N structure and to create a derived 1:N structure dynamically without modifying the source service.
For larger datasets, the resulting virtual record count should therefore be considered together with the feature-collection limit noted above.
9. Additional validation: behavior beyond the documented 50,000-feature limit
Esri documentation states that when an Arcade Data result is used to construct a feature collection, the FeatureSet used to construct that collection can contain a maximum of 50,000 features. Because the examples in this post construct a new FeatureSet with FeatureSet(Text(output)), an additional test was performed to observe what happens when that documented threshold is exceeded in the current ArcGIS Online Experience Builder environment.
A synthetic Arcade Data source was created with two fields (OBJECTID and TEST_VALUE) and no geometry. The expression generated sequential records in memory and returned them with FeatureSet(Text(output)). Tests at 49,999 and 50,000 records executed normally. The expression was then tested at 50,001 records.
At 50,001 records, the Arcade editor still evaluated the expression successfully. After the data source was added to Experience Builder, the standard Table widget reported a total of 50,001 records. A Table filter using OBJECTID = 50001 returned the expected record, confirming that the record beyond the documented 50,000 threshold was not merely included in the displayed count but was directly queryable.
The test was then repeated with 100,000 generated records. The Table widget accepted the data source, and a filter using OBJECTID = 100000 returned the expected record and TEST_VALUE = 100,000. In this tested environment, the virtual FeatureSet therefore remained usable at least up to 100,000 records.
This observation should not be interpreted as overriding Esri's documented 50,000-feature limit or as a supported guarantee for production applications. It documents only the behavior observed in ArcGIS Online Experience Builder in August 2026. The effective behavior may depend on implementation details and may change with future ArcGIS Online updates or differ in ArcGIS Enterprise. Applications should continue to be designed with the documented limit in mind unless Esri clarifies or updates the specification.
Practical implication for wide-to-long transformations. A transformation can multiply the number of virtual records produced from each source record. The tests above show that exceeding 50,000 did not immediately fail in the tested environment, but this observed behavior should not be used as a reason to design unbounded client-side transformations. Performance, memory use, browser resources, and future compatibility should also be considered.
Figure 4. Experimental validation beyond the documented 50,000-feature threshold. A virtual Arcade FeatureSet containing 100,000 generated records is used by the Experience Builder Table widget. Filtering for OBJECTID = 100000 successfully returns the corresponding record, confirming that the record is directly queryable in the tested ArcGIS Online environment (August 2026).
The result shown in Figure 4 is an observed experimental behavior and should not be interpreted as superseding Esri's documented 50,000-feature limit.
10. Why the technique may be useful beyond formatting
- Combine selected attributes from multiple tables using a common key.
- Calculate fields without modifying the source service.
- Replace sentinel values such as -99999 with ND.
- Aggregate or summarize records from 1:N relationships.
- Create derived summary tables.
- Retain a relationship key so the result reacts to map selections.
- Explore derived FeatureSets that include geometry for other workflows.
In this sense, Arcade Data can act as a lightweight virtual transformation layer between source services and Experience Builder widgets.
Conclusion
Source data → Arcade Data → Virtual FeatureSet → Calculated HTML fields → Experience Builder Table → Conditional visual formatting
The source table remains untouched. The Unicode and <span>-based approaches are the more conservative options. The block-background technique is visually powerful but should be identified explicitly as experimental.
Acknowledgements and transparency
This workflow was developed through an iterative series of experiments using ChatGPT (OpenAI) as a technical assistant.
ChatGPT helped explore possible approaches and generate and refine the Arcade code, while each behavior described in this post was tested directly in ArcGIS Online Experience Builder.
The experimental validation was important: some initial assumptions - including that HTML formatting would not be rendered by the Table widget - proved incorrect when tested in the actual widget. Those observations led to the subsequent tests with colored numeric values and the background-color block.
Test environment: ArcGIS Online Experience Builder, August 2026.
Documentation references
• Esri, ArcGIS Experience Builder documentation, “Select data” — Add data with Arcade; FeatureSet return requirements and the documented 50,000-feature limit for feature collections. Section 9 separately reports the higher counts observed experimentally in the tested environment.
• Esri, ArcGIS Experience Builder documentation, “Advanced formatting” — Arcade-based dynamic content and styling in supported widget contexts.