Every field in every node can either hold a fixed value, or an expression - a small piece of code, wrapped in {{ }}, that pulls in and transforms data from earlier in the workflow. This is the feature that turns n8n from "connect some apps" into "actually build logic," and it's simpler than it looks once you've written a few.
The basic syntax
Click the expression icon (a small "fx") next to any field, and you'll get a text box where anything inside {{ }} is evaluated as JavaScript. The most common pattern references a previous node's output by name:
{{ $('Webhook').item.json.email }}
That reads as: "from the node named Webhook, take the current item, and get its email field." You can chain this with any JavaScript method - {{ $json.name.toUpperCase() }}, {{ $json.price * 1.08 }}, or string concatenation like {{ $json.firstName + " " + $json.lastName }}.
$item() function was deprecated in favor of $('Node Name').item - if you're following an older tutorial and it doesn't work, this is usually why.The handy shortcuts
$json refers to the current item's data from the immediately preceding node - the shortest way to reference "whatever just came in." $node["Node Name"].json (or the newer $('Node Name') syntax) reaches further back to any specific node by name, not just the one directly before. $now and $today give you the current date/time without a separate node. $workflow.id and $execution.id are useful when logging or debugging which run produced what.
Mapping data by dragging, not typing
You rarely need to memorize this syntax by hand. Open the node you're configuring alongside the previous node's output panel, and drag a field directly from the output panel into the field you're editing - n8n writes the correct expression for you. This drag-to-map behavior is worth building as a habit early; typing expressions from memory is for when you need something the drag-and-drop can't express, like a transformation or conditional.
Common transformations
Formatting a date: {{ $now.format("yyyy-MM-dd") }}. Defaulting a missing value: {{ $json.name || "Unknown" }}. Building a conditional string: {{ $json.status === "paid" ? "✅ Paid" : "⏳ Pending" }}. These are all just JavaScript, evaluated inline - if you know basic JS, you already know most of what you need.
When to reach for a Set or Code node instead
Expressions are great for a single field. When you need to build several new fields at once, or the logic is getting long enough to be hard to read inline, use a Set (also called "Edit Fields") node to define multiple outputs cleanly, or a Code node when the logic genuinely needs a full script rather than a one-liner. Neither is "more advanced" than the other - they're just the right tool for how much logic you actually have.