ArcGIS Arcade Expressions
Use this skill for writing Arcade expressions for popups, renderers, labels, and calculations.
Arcade Basics
Arcade is an expression language for ArcGIS. It's used for:
-
Dynamic popup content
-
Data-driven rendering
-
Custom labels
-
Field calculations
-
Form validation
Basic Syntax
// Variables var population = $feature.population; var area = $feature.area_sqkm;
// Calculations var density = population / area;
// Return result return Round(density, 2);
Arcade in PopupTemplates
Expression Infos
const popupTemplate = { title: "{name}", expressionInfos: [ { name: "population-density", title: "Population Density", expression: "Round($feature.population / $feature.area_sqkm, 2)" }, { name: "formatted-date", title: "Formatted Date", expression: "Text($feature.created_date, 'MMMM D, YYYY')" } ], content: "Density: {expression/population-density} people/km²" };
Complex Expressions
const popupTemplate = { title: "{name}", expressionInfos: [{ name: "predominant-category", title: "Predominant Category", expression: ` var fields = [ { value: $feature.category_a, alias: "Category A" }, { value: $feature.category_b, alias: "Category B" }, { value: $feature.category_c, alias: "Category C" } ];
var maxValue = -Infinity;
var maxCategory = "";
for (var i in fields) {
if (fields[i].value > maxValue) {
maxValue = fields[i].value;
maxCategory = fields[i].alias;
}
}
return maxCategory;
`
}], content: [ { type: "text", text: "The predominant category is: {expression/predominant-category}" }, { type: "fields", fieldInfos: [{ fieldName: "expression/predominant-category" }] } ] };
Arcade in Renderers
Value Expression
const renderer = { type: "unique-value", valueExpression: ` var labor = $feature.labor_force; var notLabor = $feature.not_in_labor_force;
if (labor > notLabor) {
return "In labor force";
} else {
return "Not in labor force";
}
`, valueExpressionTitle: "Labor Force Status", uniqueValueInfos: [ { value: "In labor force", symbol: { type: "simple-fill", color: "blue" } }, { value: "Not in labor force", symbol: { type: "simple-fill", color: "orange" } } ] };
Visual Variable Expression
const renderer = { type: "simple", symbol: { type: "simple-marker", color: "red" }, visualVariables: [{ type: "size", valueExpression: "Sqrt($feature.population) * 0.1", valueExpressionTitle: "Population (scaled)", stops: [ { value: 10, size: 4 }, { value: 100, size: 40 } ] }, { type: "opacity", valueExpression: "($feature.value / $feature.max_value) * 100", valueExpressionTitle: "Percentage of max", stops: [ { value: 20, opacity: 0.2 }, { value: 80, opacity: 1 } ] }] };
Arcade in Labels
layer.labelingInfo = [{ symbol: { type: "text", color: "black", font: { size: 10 } }, labelExpressionInfo: { expression: ` var name = $feature.name; var pop = $feature.population;
if (pop > 1000000) {
return name + " (" + Round(pop/1000000, 1) + "M)";
} else if (pop > 1000) {
return name + " (" + Round(pop/1000, 0) + "K)";
}
return name;
`
}, where: "population > 50000" }];
Common Arcade Functions
Math Functions
Round(3.14159, 2) // 3.14 Floor(3.9) // 3 Ceil(3.1) // 4 Abs(-5) // 5 Sqrt(16) // 4 Pow(2, 3) // 8 Min(1, 2, 3) // 1 Max(1, 2, 3) // 3 Sum([1, 2, 3]) // 6 Mean([1, 2, 3]) // 2
Text Functions
Upper("hello") // "HELLO" Lower("HELLO") // "hello" Trim(" hello ") // "hello" Left("hello", 2) // "he" Right("hello", 2) // "lo" Mid("hello", 2, 2) // "ll" Find("l", "hello") // 2 Replace("hello", "l", "L") // "heLLo" Split("a,b,c", ",") // ["a", "b", "c"] Concatenate(["a", "b"]) // "ab"
Date Functions
Now() // Current date/time Today() // Current date Year($feature.date_field) // Extract year Month($feature.date_field) // Extract month (1-12) Day($feature.date_field) // Extract day DateDiff(Now(), $feature.date, "days") // Days between dates Text($feature.date, "MMMM D, YYYY") // Format date
Geometry Functions
Area($feature, "square-kilometers") Length($feature, "kilometers") Centroid($feature) Buffer($feature, 100, "meters") Intersects($feature, $otherFeature) Contains($feature, $point)
Conditional Functions
// IIf (inline if) IIf($feature.value > 100, "High", "Low")
// When (multiple conditions) When( $feature.type == "A", "Type A", $feature.type == "B", "Type B", "Other" )
// Decode (value matching) Decode($feature.code, 1, "One", 2, "Two", 3, "Three", "Unknown" )
Array Functions
var arr = [1, 2, 3, 4, 5];
Count(arr) // 5 First(arr) // 1 Last(arr) // 5 IndexOf(arr, 3) // 2 Includes(arr, 3) // true Push(arr, 6) // [1, 2, 3, 4, 5, 6] Reverse(arr) // [5, 4, 3, 2, 1] Sort(arr) // [1, 2, 3, 4, 5] Slice(arr, 1, 3) // [2, 3]
Feature Access
// Current feature $feature.fieldName
// All features in layer (for aggregation) var allFeatures = FeatureSet($layer); var filtered = Filter(allFeatures, "type = 'A'"); var total = Sum(filtered, "value");
// Related records var related = FeatureSetByRelationshipName($feature, "relationshipName");
// Global variables $map // Reference to map $view // Reference to view $datastore // Reference to data store
Execute Arcade Programmatically
import * as arcade from "@arcgis/core/arcade.js";
// Create profile const profile = { variables: [{ name: "$feature", type: "feature" }] };
// Compile expression const executor = await arcade.createArcadeExecutor( "Round($feature.value * 100, 2)", profile );
// Execute with feature const result = await executor.executeAsync({ $feature: graphic });
console.log("Result:", result);
Arcade in HTML (Script Tags)
<script type="text/plain" id="my-expression"> var total = $feature.value_a + $feature.value_b; var percentage = Round((total / $feature.max_value) * 100, 1); return percentage + "%"; </script>
<script type="module"> const expression = document.getElementById("my-expression").text;
const popupTemplate = { expressionInfos: [{ name: "my-calc", expression: expression }], content: "Value: {expression/my-calc}" }; </script>
TypeScript Usage
Arcade expression configurations use autocasting. For TypeScript safety, use as const :
// Use 'as const' for expression-based visualizations layer.renderer = { type: "simple", symbol: { type: "simple-marker" }, visualVariables: [{ type: "size", valueExpression: "$feature.population / $feature.area", stops: [ { value: 50, size: 4 }, { value: 500, size: 20 } ] }] } as const;
Tip: See arcgis-core-maps skill for detailed guidance on autocasting vs explicit classes.
Reference Samples
-
popuptemplate-arcade
-
Arcade expressions in PopupTemplates
-
popuptemplate-arcade-expression-content
-
Arcade expression content in popups
Common Pitfalls
Null values: Check for nulls with IsEmpty($feature.field)
Type coercion: Use Number() or Text() for explicit conversion
Case sensitivity: Arcade is case-insensitive for functions but field names match exactly
Performance: Complex expressions in renderers can slow performance
Debugging: Use Console() function to debug expressions