# How to Handle Dynamic Data in Maestro Flows: Complete Guide to Variable Interpolation

> Learn how to handle dynamic data in Maestro flows with variable interpolation using `${...}` syntax for runtime evaluation by the Env.evaluateScripts engine. Master your Maestro tests.

- Repository: [Maestro/Maestro](https://github.com/mobile-dev-inc/Maestro)
- Tags: how-to-guide
- Published: 2026-03-20

---

**Maestro handles dynamic data by treating every string as a JavaScript template that uses `${...}` syntax for interpolation, evaluated at runtime by the `Env.evaluateScripts` engine.**

The mobile-dev-inc/Maestro framework provides a powerful variable interpolation system that allows you to inject, compute, and reuse dynamic values throughout your test flows using JavaScript-backed template syntax. This enables data-driven testing where credentials, timestamps, calculated values, and environment-specific configuration can be injected at execution time.

## Understanding Maestro's Variable Interpolation Engine

At the core of Maestro's dynamic data handling is the **JS-engine-backed `evaluateScripts`** system implemented in [`maestro-orchestra-models/src/main/java/maestro/orchestra/util/Env.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-orchestra-models/src/main/java/maestro/orchestra/util/Env.kt). This engine walks through every string in your flow, identifies unescaped `${...}` patterns, evaluates the inner content using the embedded **JavaScript engine** (`JsEngine`), and substitutes the result.

The interpolation process respects escaped sequences (`\${...}`), leaving them untouched after removing the backslash. This architecture means any valid JavaScript expression can be embedded inside the braces—arithmetic operations, string concatenation, ternary logic, or calls to exposed library functions like `Date.now()`.

## Setting Variables in Maestro Flows

Maestro provides three primary mechanisms for defining variables that become available for interpolation.

### CLI Environment Variables

Pass key-value pairs directly from the command line using the `--env` flag. According to [`Env.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/Env.kt) (lines 28-31), these values are automatically merged into the execution context via the `MaestroCommand.withEnv` extension.

```bash
maestro test flow.yaml --env USERNAME=jdoe,PASSWORD=secret123

```

All subsequent `${USERNAME}` and `${PASSWORD}` placeholders in **flow.yaml** will resolve to the supplied values.

### Flow-Level Environment Variables

Define static variables at the top of your flow file using the `env:` map. This block is automatically converted into a `DefineVariablesCommand` during parsing.

```yaml

# flow.yaml

env:
  API_TOKEN: abcdef123456
  LANG: en

- launchApp: com.example.myapp
- inputText:
    selector: { text: "Username" }
    text: "${USERNAME}"
- inputText:
    selector: { text: "Password" }
    text: "${PASSWORD}"
- assertVisible:
    selector: { text: "Welcome ${USERNAME}" }

```

### Runtime Variables with DefineVariablesCommand

Create variables dynamically during execution using the `defineVariables` command (implemented in [`maestro-orchestra-models/src/main/java/maestro/orchestra/Commands.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-orchestra-models/src/main/java/maestro/orchestra/Commands.kt) lines 52-70). This is ideal for generating timestamps, UUIDs, or computed values mid-flow.

```yaml
- defineVariables:
    SESSION_ID: "${java.util.UUID.randomUUID().toString()}"
    TODAY: "${new Date().toISOString().split('T')[0]}"
- tapOn:
    selector: { text: "Start session ${SESSION_ID}" }
- assertVisible:
    selector: { text: "Today is ${TODAY}" }

```

## Using JavaScript Expressions for Dynamic Values

Because the interpolation engine is JavaScript-backed, you can perform complex computations directly inside `${...}` placeholders.

```yaml
- inputText:
    selector: { text: "Discount" }
    text: "${(price * quantity).toFixed(2)}"

```

Assuming `price` and `quantity` were defined earlier, the expression evaluates to a formatted string (e.g., `"19.99"`) before being sent to the device.

## Reserved Variables and Escaping Syntax

### Accessing System-Injected Variables

Maestro automatically injects reserved environment variables through `Env.withDefaultEnvVars` (defined in [`Env.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/Env.kt) lines 33-48). These include:

- `MAESTRO_FILENAME` – The current flow file name
- `MAESTRO_DEVICE_UDID` – The target device identifier
- `MAESTRO_SHARD_ID` – Shard identifier for parallel execution
- `MAESTRO_SHARD_INDEX` – Current shard index

```yaml
- assertVisible:
    selector: { text: "Running on shard ${MAESTRO_SHARD_INDEX}" }

```

**Important:** Reserved variables like `MAESTRO_SHARD_ID` and `MAESTRO_SHARD_INDEX` cannot be overridden by user-defined values or CLI arguments.

### Escaping Literal Syntax

To prevent evaluation and include literal `${...}` text in your selectors, prepend a backslash. The `Env.evaluateScripts` function removes the escape character while preserving the literal braces.

```yaml
- tapOn:
    selector: { text: "\${STATIC_PLACEHOLDER}" }

```

This outputs the literal string `${STATIC_PLACEHOLDER}` without JavaScript evaluation.

## Execution Flow of Variable Resolution

According to the orchestration logic in [`Orchestra.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/Orchestra.kt), Maestro performs the following steps for every command:

1. **Variable collection** – CLI `--env`, flow-level `env:` blocks, and any inline `DefineVariablesCommand` instances are merged using `withEnv`.
2. **Default injection** – `Env.withDefaultEnvVars` adds `MAESTRO_FILENAME`, `MAESTRO_DEVICE_UDID`, shard identifiers, and other system values.
3. **Script evaluation** – Each command's `evaluateScripts(jsEngine)` method walks its fields (e.g., `text`, `selector`, `label`) and runs `Env.evaluateScripts` to resolve `${...}` patterns.
4. **Command execution** – The fully interpolated command (with concrete values) is passed to the iOS or Android driver for interaction.

Flow-wide configuration commands defined in [`MaestroConfig.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/MaestroConfig.kt) (lines 18-24) for `onFlowStart` and `onFlowComplete` also support `${...}` interpolation and follow the same evaluation pipeline.

## Summary

- **Variable interpolation** in Maestro uses JavaScript template syntax `${...}` evaluated by `Env.evaluateScripts`.
- **Three definition methods**: CLI `--env` flags, flow-level `env:` maps (converted to `DefineVariablesCommand`), and runtime `defineVariables` steps.
- **JavaScript expressions** are fully supported inside interpolation blocks, enabling dynamic calculations.
- **Reserved variables** like `MAESTRO_SHARD_INDEX` are injected automatically and cannot be overridden.
- **Escape literal** `${...}` by prefixing with a backslash: `\${...}`.

## Frequently Asked Questions

### Can I override the MAESTRO_SHARD_INDEX variable?

No. According to [`Env.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/Env.kt) lines 33-48, variables like `MAESTRO_SHARD_ID` and `MAESTRO_SHARD_INDEX` are reserved internal variables injected automatically by the orchestration engine. These cannot be overridden by CLI arguments, flow-level environment variables, or `defineVariables` commands.

### How do I include a literal dollar-brace string without triggering JavaScript evaluation?

Use a leading backslash to escape the interpolation syntax. The `Env.evaluateScripts` function in [`Env.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/Env.kt) removes the backslash while leaving the literal `${...}` text intact. For example: `text: "\${UNINTERPOLATED}"` renders as `${UNINTERPOLATED}`.

### Can I use any JavaScript library or npm package inside the interpolation?

You can use any JavaScript expressions or standard library functions exposed by the embedded `JsEngine`, such as `Date`, `Math`, or `java.util.UUID`. However, you cannot import external npm packages or Node.js-specific modules. The evaluation context is sandboxed and limited to what the Maestro JS engine exposes.

### What is the difference between flow-level `env:` and the `defineVariables` command?

The `env:` block at the top of a flow file defines static variables before execution begins and is processed via `MaestroCommand.withEnv`. The `defineVariables` command (implemented by `DefineVariablesCommand` in [`Commands.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/Commands.kt)) executes at runtime during the flow, allowing you to create variables based on previous test state or dynamic JavaScript evaluation (like generating UUIDs or timestamps).