Arcade is a lightweight and secure expression language written for use with ArcGIS. Like other expression languages, it can perform mathematical calculations and evaluate logical statements. It was designed specifically for creating custom visualizations, popups, and labeling expressions with ArcGIS, allowing users to write, share, and execute expressions in ArcGIS Pro, ArcGIS Online, the ArcGIS Runtime APIs, and the ArcGIS API for JavaScript.

What makes Arcade particularly unique to other expression and scripting languages is its inclusion of geometry functions. These geometry operations allow you to calculate areas and lengths, test topological relationships, and perform simple overlay operations.


In many respects Arcade's syntax is similar to JavaScript, allowing you to declare variables, perform logical operations, take advantage of built-in functions, and write custom functions. However, there are key differences between the two languages. Read the full Arcade documentation including guides and the function reference for more details on how to write an Arcade expression. You also have access to a playground that provides an environment for testing custom scripts based on your data.

Global variables may be used within Arcade expressions, giving the expression access to external data. Global variables contain external values that are passed to a script during its execution – based on the execution profile. The $feature global variable allows you to access field values for features in a FeatureLayer. Each field value is also considered a global variable. Field values are referenced using the following syntax:


This makes it easy to perform simple calculations using field values at runtime.

// calculates the % of voters who voted for a
// democratic candidate
($feature.DEM_VOTES / $feature.TURNOUT ) * 100

Arcade is only executed within the context, or profile, in which it is understood. Within JavaScript apps, Arcade expressions are always referenced as a string value. You may use Arcade to write simple single-line expressions, or more complex multi-line expressions.

When writing single-line expressions, you can simply wrap it in double or single quotes.

renderer.valueExpression = "Round( ($feature.AGE_18UP / $feature.TOTAL_POP) * 100 )";

You can use template literals (ES6 or later) to write multi-line Arcade expressions.

renderer.valueExpression = `
  var republican = $feature.MP06025a_B;
  var democrat = $feature.MP06024a_B;
  var independent = $feature.MP06026a_B;

  var parties = [ republican, democrat, independent ];
  var total = Sum(parties);
  var max = Max(parties);
  return (max / total) * 100;

Keep in mind that Arcade text supports template literals. Since Arcade expressions are stored as strings in the ArcGIS API for JavaScript, they are often written using template literals in JavaScript. You can still use Arcade template literals inside a JavaScript template literal, but you will need to escape key characters such as backticks `and dollar signs$` for the Arcade template literals to be recognized. For example, note the Arcade expression that uses a template literal within a JavaScript template literal in a PopupTemplate:

layer.popupTemplate = {
  content: "{expression/percent-unemployed}",
  expressionInfos: [
      name: "percent-unemployed",
      title: "Percent unemployed",
      expression: `
        var unemploymentRate = ( $feature.UNEMP_CY / $feature.LABOR_FORCE ) * 100;
        var population = $feature.POP_16UP;
        var populationNotWorking = ( ( $feature.UNEMP_CY + $feature.NOT_LABORFORCE_16 ) / $feature.POP_16UP ) * 100;

        // returns a string built using an Arcade template literal

        return \`\${$feature.COUNTY} County

        - Unemployment rate: \${Text(unemploymentRate, "##.#")}%
        - % population not working: \${Text(populationNotWorking, "##.#")}%
        - Population: \${Text(population, "#,###")}\`

The Summarize intersecting points in a popup sample demonstrates another example of Arcade template literals.

If you aren't writing ES6 code, you can place the expression in a separate <script> tag outside the JavaScript, and set the type to text/plain with a unique ID to reference the script in the appropriate place within JavaScript.

<script type="text/plain" id="adult-population">
  var republican = $feature.MP06025a_B;
  var democrat = $feature.MP06024a_B;
  var independent = $feature.MP06026a_B;

  var parties = [ republican, democrat, independent ];
  var total = Sum(parties);
  var max = Max(parties);
  return (max / total) * 100;

Then reference the script as a string value by calling the document.getElementById() method.

renderer.valueExpression = document.getElementById("adult-population").text;

See the example snippets below and the Create a custom visualization using Arcade sample for more context.


Arcade was designed for use in several profiles. A profile is the context and rules defining how and where the expression should be defined and executed. In the ArcGIS API 4.19 for JavaScript, Arcade supports the following profiles: Constraint, Feature Z, Labeling, Popups, and Visualization.


The Constraint profile is implemented in the FeatureForm widget, allowing you to control the visibility of fields and grouped fields during editing workflows. Instead of displaying all of the specified fields in various field configurations, you can configure certain fields to display based on whether they meet a condition specified in an Arcade expression. The expression must return a Boolean value and be set on the visibilityExpression property found on both FieldConfig and FieldGroupConfig classes. Alternatively, the same visibilityExpression property is found in the form's Element class.

See the Advanced Attribute Editing sample, which demonstrates the following example.

featureForm.fieldConfig = [{
  name: "status",
  editable: false, // not an editable field
  label: "Issue status",
  description: "E.g. submitted, received, in progress, or completed."
}, {
  name: "resolution",
  label: "Resolution",
  editable: false,
  description: "Resolution if status is 'Completed'",
  visibilityExpression: "($feature.status == 'Completed') && (!(IsEmpty($feature.resolution)))"

The first field configuration displays a field labeled Issue status. If the value is either Completed or In Progress, the second field labeled Resolution will not display. Based on the status field, one or two grouped field configurations display.

The FeatureForm widget only supports the $feature global as defined in the Constraint Profile.

In addition to the above-referenced sample, see the Update Feature Attributes sample, which demonstrates setting the visibilityExpression directly in a FeatureForm's formTemplate.

Feature Z

In 3D SceneViews you can set custom Z values on features using Arcade expressions. Although features can have Z values inside their geometry, you might want to calculate or replace them based on an attribute value, or derive it from a formula using the Z value. You may also have features that don't contain Z values in their geometries, but have Z-related information stored in an attribute field. In these cases you can set an expression in the featureExpressionInfo.expression property. As an example, the Elevation options sample shows how to change the Z value of points using Arcade:

layer.elevationInfo = {
  mode: "absolute-height",
  featureExpressionInfo: {
    expression: "Geometry($feature).z + $feature.HEIGHT"
  unit: "meters"

In the example above, a field attribute HEIGHT is added to the Z value of the geometry to set the final graphic's elevation. For polyline or polygon features, all the vertices of each feature will have a Z value returned by expression.


Arcade is used to create label expressions for features in a FeatureLayer or SceneLayer. Starting at version 4.5 of the API, this is the only supported method for labeling features.

You must add at least one LabelClass to the labelingInfo property of the layer. The Arcade expression must be passed as a string value to the expression property of the LabelClass's labelExpressionInfo object.

// returns the value of a field in the layer
// the value of this field will be the label for each feature
const arcade = "$feature.STATION_NAME";

// this object autocasts as new LabelClass()
const labelClass = {
  // set the arcade expression to the `expression` property of labelExpressionInfo
  labelExpressionInfo: {
    expression: arcade
  labelPlacement: "below-right",
  minScale: 2500000,
  symbol: {
    type: "label-3d",
    symbolLayers: [{
      type: "text",
      material: { color: "white" },
      halo: {
        color: "black",
        size: 1
      size: 8

// set the label class to the feature layer
featureLayer.labelingInfo = [ labelClass ];

Label expressions written in Arcade may be more complex, containing multiple lines that perform mathematical and logical operations. For example, the Multi-line labels demonstrates a how to construct a more complex multi-line label expression. This expression assigns two numeric field values to their own variables, evaluates them, and returns a string value. Arcade's When() function is used to evaluate the wind direction (between 0-360 degrees), which returns the associated compass direction of either N, NE, E, SE, S, SW, W, or NW. If the wind speed is 0, then no direction is returned. The final line of the expression returns the label, which is the value of the WIND variable.

<script type="text/plain" id="wind-direction">
  var DEG = $feature.WIND_DIRECT;
  var SPEED = $feature.WIND_SPEED;
  var DIR = When( SPEED == 0, '',
    (DEG < 22.5 && DEG >= 0) || DEG > 337.5, 'N',
     DEG >= 22.5 && DEG < 67.5, 'NE',
     DEG >= 67.5 && DEG < 112.5, 'E',
     DEG >= 112.5 && DEG < 157.5, 'SE',
     DEG >= 157.5 && DEG < 202.5, 'S',
     DEG >= 202.5 && DEG < 247.5, 'SW',
     DEG >= 247.5 && DEG < 292.5, 'W',
     DEG >= 292.5 && DEG < 337.5, 'NW', '' );
  var WIND = SPEED + ' mph ' + DIR;
  return WIND;

There are many additional Arcade functions useful for labeling, including text functions that provide logic for text formatting. Be sure to check out the full Arcade documentation for more information regarding these built-in functions.


Arcade expressions can be referenced within the content of a PopupTemplate. Similar to the visualization profile, this is useful for situations when you want to display data that isn't present as an attribute value in your FeatureLayer instance.

For example, the Reference Arcade expressions in PopupTemplate sample displays a layer containing labor statistics for each U.S. county. Some attributes include unemployment rate, population, and the number of people participating in the labor force. It does not have an attribute for labor force participation rate. We can use Arcade to calculate that for us at runtime.

// labor force participation rate
Round(($feature.CIVLBFR_CY / $feature.POP_16UP)*100,2)

The values returned from this expression can be used to visualize the layer and/or displayed in the layer's popupTemplate. To view the value in the popup, we must reference it in the expressionInfos property of the PopupTemplate and assign it a name and a title.

layer.popupTemplate = {
  expressionInfos: [{
    name: "participation-rate",
    title: "% of population 16+ participating in the labor force",
    expression: "Round(($feature.CIVLBFR_CY / $feature.POP_16UP)*100,2)"
  content: "In {NAME} county, {expression/participation-rate}% of the population"
    + " participates in the labor force."

Notice that once the expression exists in the expressionInfos property, you can reference the value returned from the expression using the {expression/expression-name} placeholder template within the content of the PopupTemplate. The Popup's content would display the following after the user clicked a feature representing Greenlee, AZ:


You can also reference values returned from Arcade expressions inside the fieldInfos property of the PopupTemplate's content so they can be displayed in tabular format. Just reference the name of the expression in the fieldName property of the object. Remember to use the expression/expression-name syntax.

layer.popupTemplate = {
  expressionInfos: [{
    name: "participation-rate",
    title: "% of population 16+ participating in the labor force",
    expression: "Round(($feature.CIVLBFR_CY / $feature.POP_16UP)*100,2)"
  content: [{
    type: "fields",
    fieldInfos: [{
      fieldName: "expression/participation-rate"

The popup will display the following:


Note that you can also take advantage of the formatting options of the PopupTemplate's fieldInfos property to format numbers returned from expressions.

View the Reference Arcade expressions in PopupTemplate sample to see this workflow in the context of a live sample application.


In the Visualization profile, Arcade allows you to calculate values for each feature in a layer at runtime and use those values as the basis for a data-driven visualization. This is an alternative approach to creating data-driven visualizations based on a single field value in the layer. To accomplish this, an Arcade expression may be passed to the valueExpression property in ClassBreaksRenderer, UniqueValueRenderer or any of the visual variables: color, size, opacity, and rotation instead of referencing a field/normalizationField.

When used in a ClassBreaksRenderer or any of the visual variables, the expression must evaluate to a number. Expressions may evaluate to either strings or numbers in UniqueValueRenderer.

In the example below, an Arcade expression is used in the valueExpression property of a UniqueValueRenderer. In this case, we are creating a visualization for a FeatureLayer representing U.S. counties. The service has three fields that identify the number of republicans, democrats, and independent/non-party voters in each county. We would like to visualize each county based on which party outnumbers the others. Since the service does not contain a field indicating the predominant party, we can write an Arcade expression to identify that for each feature.

First, write the Arcade expression in a script tag with a unique ID.

<script type="text/plain" id="winning-party">
  // Write the expression and reference the value
  // of each field with a meaningful variable name within
  // the expression. Then calculate the max number with
  // the Max() function and use Decode() to return a string
  // value representing the party whose field value matches
  // the max value.

  var republican = $feature.MP06025a_B;
  var democrat = $feature.MP06024a_B;
  var independent = $feature.MP06026a_B;
  var parties = [republican, democrat, independent];
  // Decode() and Max() are built-in Arcade functions
  return Decode( Max(parties),
    republican, 'republican',
    democrat, 'democrat',
    independent, 'independent',

Then reference the script as a string value using document.getElementById() within the appropriate valueExpression property in the JavaScript.

// Assign the expression to the `valueExpression` property and
// set up the unique value infos based on the decode values
// you set up in the expression.

const winnerArcade = document.getElementById("winning-party").text;

const renderer = new UniqueValueRenderer({
  valueExpression: winnerArcade,
  valueExpressionTitle: "Counties by dominant party among registered voters",
  uniqueValueInfos: [{
    value: "democrat",
    symbol: createSymbol("#00c3ff"),
    label: "Democrat"
  }, {
    value: "republican",
    symbol: createSymbol("#ff002e"),
    label: "Republican"
  }, {
    value: "independent",
    symbol: createSymbol("#faff00"),
    label: "Independent/non-affiliated"

You can also add an opacity visual variable to the renderer to visualize the relative strength of the predominant party in each county. Counties where more people come from a single party will be drawn with high opacity, while those where the proportion of people from each party are relatively equal will be drawn with low opacity.

First, write the expression in a <script> tag.

<script type="text/plain" id="strength">
  // Write the expression and reference the value
  // of each field with a meaningful variable name within
  // the expression. Then calculate the max number with
  // the Max() function and the total using Sum().
  // Calculate the share of the max population within the
  // county. This value will be between 33 - 100 and will
  // be used to determine the feature's opacity.
  // Note the value is explicitly returned; it could also
  // be implicitly returned like the previous example

  var republican = $feature.MP06025a_B;
  var democrat = $feature.MP06024a_B;
  var independent = $feature.MP06026a_B;
  var parties = [republican, democrat, independent];
  var total = Sum(parties);
  var max = Max(parties);

  return (max / total) * 100;

Then reference it in JavaScript as a string value.

// Assign the expression to the `valueExpression` property and
// set up the unique value infos based on the decode values
// you set up in the expression.

const strengthArcade = document.getElementById("strength").text;

const opacityVV = {
  type: "opacity",
  valueExpression: strengthArcade,
  stops: [
    { value: 33, opacity: 0.1 },
    { value: 50, opacity: 1.0 }

// Add the visual variable to the renderer

renderer.visualVariables = [ opacityVV ];

View the Create a custom visualization using Arcade sample to see this example live.

You can use Arcade playground to build, debug, and test an Arcade expression within a browser based on field values imported from an input feature service. It also includes the ability to debug complex scripts with a Console() function.