Guide to targeting with JEXL

For a more in-depth explanation of JEXL syntax you can read the Normady project docs.

How to write JEXL targeting expressions

A message needs to contain the targeting property (JEXL string) which is evaluated against the provided attributes. Examples:

{
  "id": "7864",
  "content": {...},
  // simple equality check
  "targeting": "usesFirefoxSync == true"
}

{
  "id": "7865",
  "content": {...},
  // using JEXL transforms and combining two attributes
  "targeting": "usesFirefoxSync == true && profileAgeCreated > '2018-01-07' | date"
}

{
  "id": "7866",
  "content": {...},
  // targeting addon information
  "targeting": "addonsInfo.addons['activity-stream@mozilla.org'].name == 'Activity Stream'"
}

{
  "id": "7866",
  "content": {...},
  // targeting based on time
  "targeting": "currentDate > '2018-08-08' | date"
}

Available JEXL transforms and operators

Firefox extends standard JEXL with additional transforms and a binary operator, defined in FilterExpressions.sys.mjs. Transforms are applied using the pipe operator (|), e.g. someValue | transformName.

date

Converts a date string into a Date object.

currentDate | date > "2025-05-06" | date

stableSample

Hashes the input and returns true if the hash falls below the given rate (0.0–1.0).

// Target approximately 10% of users, consistently
clientID | stableSample(0.1)

bucketSample

Hashes the input and returns true if it falls within the given bucket range (start to start + count) out of total buckets. The range wraps around if it exceeds total, so bucketSample(70, 50, 100) checks buckets 70–99 and 0–19.

// Target 500 out of 10,000 buckets
userId | bucketSample(0, 500, 10000)

preferenceValue

Returns the current value of a Firefox preference. Supports string, integer, and boolean preference types. Returns defaultValue if the preference does not exist, and throws if the preference exists but is of an unrecognised type.

"browser.newtabpage.enabled" | preferenceValue
"some.pref" | preferenceValue(true)

preferenceIsUserSet

Returns true if the user has explicitly changed the preference from its default value. A preference reset to its default value is considered not user-set, even if the current value matches what the user previously set.

"browser.newtabpage.enabled" | preferenceIsUserSet

preferenceExists

Returns true if the preference exists with any valid type (string, integer, or boolean). Returns false for preferences with an invalid or unrecognised type.

"some.optional.pref" | preferenceExists

keys

Returns an array of the enumerable keys of an object, or undefined if the input is not an object.

providerCohorts | keys

length

Returns the length of an array, or undefined if the input is not an array.

topFrecentSites | length > 5

mapToProperty

Given an array of objects, returns a new array of a single named property extracted from each element.

topFrecentSites | mapToProperty("host")

intersect

Returns the elements present in both arrays, or undefined if either argument is not an array. Unlike the transforms above, this is used as an infix operator rather than with a pipe.

topFrecentSites | mapToProperty("host") intersect ["amazon.com", "ebay.com", "etsy.com"]

regExpMatch

Matches a string against a regular expression. Returns an array of matches, or null if there are none. Accepts an optional flags argument.

currentURL | regExpMatch("^https://example\\.com/")
currentURL | regExpMatch("^https://example\\.com/", "i")

versionCompare

Compares two version strings. Returns 0 if equal, a negative number if the left is lower, or a positive number if the left is higher.

version | versionCompare("120.0") >= 0