# How to Use Expressions in n8n to Reference Dynamic Data

- Tool: n8n
- Difficulty: Beginner
- Time required: 15-20 minutes
- Compatibility: n8n 1.0+, all installation methods
- Last updated: March 2026

## TL;DR

n8n expressions use double curly braces {{ }} to dynamically reference and transform data inside node parameters. Access the current item with {{ $json.fieldName }}, reference other nodes with {{ $('NodeName').first().json.field }}, and use Luxon for dates and JMESPath for complex JSON queries. Expressions evaluate at runtime and let you build dynamic, data-driven workflows.

## How to Use Expressions in n8n

Expressions are the backbone of dynamic data handling in n8n. They let you reference data from previous nodes, transform values, perform calculations, and build strings dynamically — all within node parameter fields. Instead of hardcoding values, expressions pull data at runtime from the workflow execution context, making your workflows flexible and reusable.

## Before you start

- A running n8n instance with the workflow editor open
- At least one workflow with nodes that produce output data
- Basic understanding of JSON data structures

## Step-by-step guide

### 1. Open the expression editor for a parameter

Most node parameters support expressions. To switch a parameter from fixed value mode to expression mode, click the expression toggle button (the curly braces icon or the dropdown selector) next to the parameter field. Once in expression mode, the field turns into an expression editor where you can type {{ }} expressions. The editor shows a live preview of the evaluated result based on the last execution data.

**Expected result:** The parameter field switches to expression mode with a different background color and shows a preview of the expression result below the field.

### 2. Reference data from the current item

The most common expression pattern is referencing fields from the current input item. Use {{ $json.fieldName }} to access any field from the incoming JSON data. For nested objects, chain the properties: {{ $json.user.email }}. For arrays, use bracket notation: {{ $json.items[0].name }}. The $json variable always refers to the json property of the current item being processed.

```
// Access a top-level field
{{ $json.name }}
// Output: "Alice"

// Access a nested field
{{ $json.address.city }}
// Output: "New York"

// Access an array element
{{ $json.tags[0] }}
// Output: "important"

// Access the full item including metadata
{{ $json }}
// Output: the entire JSON object
```

**Expected result:** The expression preview shows the resolved value from the input data. The field dynamically pulls the correct value during execution.

### 3. Reference data from other nodes in the workflow

To access output data from a specific node (not just the directly connected one), use the $() function with the node name. This is useful when you need data from a node earlier in the workflow that is not the immediate predecessor. Always use .first() or .last() to select a specific item, or .all() to get all items as an array.

```
// Get a field from the first item of a specific node
{{ $('HTTP Request').first().json.status }}
// Output: 200

// Get a field from the last item of a specific node
{{ $('Webhook').last().json.body.email }}
// Output: "user@example.com"

// Get the number of items output by a node
{{ $('Fetch Orders').all().length }}
// Output: 15

// Access item at a specific index
{{ $('Code Node').all()[2].json.name }}
// Output: "Third Item"
```

**Expected result:** The expression resolves to data from the specified node, regardless of where in the workflow it is located relative to the current node.

### 4. Use built-in date and time functions with Luxon

n8n includes the Luxon library for date and time manipulation. Use {{ $now }} for the current date and time as a Luxon DateTime object, and {{ $today }} for today's date at midnight. You can chain Luxon methods to format dates, add or subtract time, and compare timestamps.

```
// Current date and time in ISO format
{{ $now.toISO() }}
// Output: "2026-03-27T14:30:00.000+00:00"

// Today's date formatted
{{ $today.toFormat('yyyy-MM-dd') }}
// Output: "2026-03-27"

// Yesterday's date
{{ $now.minus({ days: 1 }).toFormat('yyyy-MM-dd') }}
// Output: "2026-03-26"

// Add 2 hours to current time
{{ $now.plus({ hours: 2 }).toISO() }}

// Parse a date string from input data
{{ DateTime.fromISO($json.createdAt).toFormat('dd/MM/yyyy') }}
// Output: "27/03/2026"

// Check if a date is in the past
{{ DateTime.fromISO($json.expiresAt) < $now }}
```

**Expected result:** Date expressions resolve to formatted date strings or boolean comparisons that you can use in node parameters and conditions.

### 5. Use JMESPath for complex JSON queries

For complex data extraction from nested JSON structures, n8n supports JMESPath expressions via the $jmespath() function. JMESPath is especially useful for filtering arrays, selecting specific fields from nested objects, and flattening data structures without writing custom code.

```
// Extract all names from an array of objects
{{ $jmespath($json.users, '[*].name') }}
// Output: ["Alice", "Bob", "Carol"]

// Filter array items where status is active
{{ $jmespath($json.orders, '[?status==`active`]') }}
// Output: array of orders with status "active"

// Get the first matching item's email
{{ $jmespath($json.contacts, '[?role==`admin`] | [0].email') }}
// Output: "admin@example.com"

// Flatten nested arrays
{{ $jmespath($json, 'departments[*].employees[*].name[]') }}
// Output: flat array of all employee names across departments
```

**Expected result:** Complex JSON data is extracted and transformed according to your JMESPath query, returning the filtered or restructured result.

### 6. Combine expressions with string interpolation

You can mix static text with expressions to build dynamic strings. Place expressions inside {{ }} within a regular text string. This is commonly used for composing email bodies, API URLs, notification messages, and log entries that include dynamic data from the workflow.

```
// Build a dynamic message
Hello {{ $json.name }}, your order #{{ $json.orderId }} has been shipped!

// Build a dynamic API URL
https://api.example.com/users/{{ $json.userId }}/orders?status={{ $json.status }}

// Multi-expression string
Processed {{ $('Fetch Data').all().length }} items on {{ $now.toFormat('yyyy-MM-dd') }}
```

**Expected result:** The final string contains the resolved values from all expressions, creating a complete dynamic message or URL.

## Complete code example

File: `expression-examples-code-node.js`

```javascript
// Code Node: Demonstrates n8n expression equivalents in JavaScript
// This shows how expression concepts map to Code node logic
// when you need more complex transformations.

const items = $input.all();
const results = [];

for (const item of items) {
  const data = item.json;
  
  // Equivalent of {{ $json.fieldName }}
  const customerName = data.name;
  
  // Equivalent of {{ $('Other Node').first().json.field }}
  const webhookData = $('Webhook').first().json;
  
  // Equivalent of {{ $now.toFormat('yyyy-MM-dd') }}
  const today = new Date().toISOString().split('T')[0];
  
  // String interpolation equivalent
  const message = `Hello ${customerName}, your request from ${today} is being processed.`;
  
  // Array filtering (JMESPath equivalent)
  const activeItems = (data.items || []).filter(
    item => item.status === 'active'
  );
  
  // Nested access with fallback
  const email = data?.contact?.email || 'no-email@example.com';
  
  results.push({
    json: {
      customerName,
      email,
      message,
      activeItemCount: activeItems.length,
      activeItems,
      processedAt: new Date().toISOString(),
      source: webhookData?.source || 'unknown'
    }
  });
}

return results;
```

## Common mistakes

- **Typing {{ $json.field }} in fixed value mode instead of switching to expression mode** — undefined Fix: Click the expression toggle next to the parameter field before typing your expression. In fixed mode, the curly braces are treated as literal text.
- **Using $('Node Name') without .first() or .all(), causing undefined errors** — undefined Fix: Always use .first().json, .last().json, or .all() after $('Node Name') to select specific items. The $() function returns a reference, not the data directly.
- **Using regular quotes instead of backticks for string literals in JMESPath filters** — undefined Fix: JMESPath uses backticks for string values inside filter expressions: [?status==`active`], not [?status=='active'].
- **Referencing a node by the wrong name after renaming it** — undefined Fix: The $() function uses the node's current display name. If you rename a node, update all expressions in other nodes that reference it.

## Best practices

- Always use the expression editor preview to verify your expression resolves correctly before saving
- Use optional chaining in expressions like {{ $json.user?.email }} to avoid errors when fields might be missing
- Reference nodes by their exact display name — node names are case-sensitive in $() expressions
- Prefer $json for simple field access and $jmespath() for complex filtering and transformation
- Use Luxon methods for all date operations instead of JavaScript Date to ensure consistent timezone handling
- Keep expressions simple and readable — move complex logic to a Code node instead of writing long expressions
- Test expressions with different input data shapes to ensure they handle edge cases gracefully

## Frequently asked questions

### What is the difference between {{ $json }} and {{ $input }}?

$json is a shortcut for the json property of the current input item. $input gives you access to the full input object including metadata. For most cases, $json is what you need to reference field values.

### Can I use JavaScript functions inside expressions?

Yes. You can use standard JavaScript methods like .toUpperCase(), .split(), .map(), Math.round(), and others inside expression brackets. For example, {{ $json.name.toUpperCase() }} converts the name to uppercase.

### How do I handle null or undefined values in expressions?

Use optional chaining with the question mark operator: {{ $json.user?.email }}. You can also provide a fallback with the OR operator: {{ $json.email || 'no-email' }}.

### Can expressions access environment variables?

Yes. Use {{ $env.VARIABLE_NAME }} to access environment variables that are set in your n8n configuration. This is useful for referencing API base URLs or other configuration values.

### Why does my expression show [Object object] instead of the value?

This happens when the expression returns an object or array instead of a primitive value. Drill deeper into the object with additional property access, like {{ $json.result.data.value }} instead of {{ $json.result }}.

### How do I debug expressions that return unexpected results?

Use the expression editor preview, which shows the evaluated result based on the most recent execution data. You can also add a Code node to log intermediate values and inspect the data structure.

---

Source: https://www.rapidevelopers.com/n8n-tutorial/how-to-use-expressions-in-n8n
© RapidDev — https://www.rapidevelopers.com/n8n-tutorial/how-to-use-expressions-in-n8n
