The n8n IF node:
routing, conditions, real examples.
I spent an hour last month debugging a Stripe routing workflow that was silently dropping high-value charges. The IF node condition looked right. The data type was wrong. It was a string pretending to be a number. That's not the most embarrassing IF node failure I've had. But it's the most recent one. Here's everything I've figured out from building these in production every day.
I run n8n workflows 24/7: email triage, payment hooks, content pipelines. The IF node is in more of those workflows than any other single node, and I've broken it in every way you can break it. What's below isn't a docs summary. It's what I've actually figured out from watching these things fail in production.
The IF node routes, it doesn't filter. That distinction changes how you wire everything after it.
Every item that enters an IF node goes to one of two outputs: true or false. That's the whole job. You define a condition (or a set of conditions) and n8n checks each incoming item against them. Items that match go to the true branch. Items that don't go to the false branch.
This is routing, not filtering. The IF node doesn't drop anything. Both branches can receive items and do entirely different things with them. I'll cover the filter-vs-IF distinction in its own section because people get this wrong constantly and it matters for keeping your canvas readable.
In n8n's visual editor: the true output is at the top of the node, the false output is at the bottom. You drag wires from each output to whatever nodes should handle those items next. What actually happens depends entirely on what you wire up — including whether you wire up both sides at all.
If you only wire up the true branch and forget the false one, items that don't match just disappear. No error, no warning. The workflow continues on the true side only. This is the most common IF node mistake and I'll cover it in more detail later.
AND, OR, and nested conditions: the logic is simple but type mismatches will end you
The simplest IF condition is a single check: does this field equal this value? One condition, two outputs.
{{ $json.status }} equals "active"
When you need more precision, you add multiple conditions. n8n gives you two operators:
- AND: all conditions must be true for the item to go to the true branch
- OR: any one condition being true sends the item true
You can also mix them. n8n's condition editor lets you group conditions — so you could say "condition A AND (condition B OR condition C)." It's not the most intuitive UI once you get past two conditions, but it works.
{{ $json.amount }} greater than 500
AND
{{ $json.currency }} equals "usd"
{{ $json.subject }} contains "URGENT"
OR
{{ $json.priority }} equals "high"
One thing to watch: n8n's condition operators depend on data type. Strings get "equals," "contains," "starts with," "ends with," and similar text checks. Numbers get "greater than," "less than," "equals." Booleans get "is true" / "is false." If the field coming in is a string that happens to look like a number — "42" instead of 42 — then numeric operators will give wrong results or fail silently. This comes up more than you'd expect with webhook data. More on it in the mistakes section.
Three IF node patterns from my live stack, with what each one actually catches
1. Email triage: routing by category
My email triage workflow runs every 5 minutes. It pulls new Gmail messages, passes them through a classification step (an AI node that reads the subject and body), and by the time an item reaches the IF node, it has a category field: either URGENT, FYI, or JUNK.
The IF node has one job: does category equal URGENT? If yes, a Telegram message hits my phone immediately. If no, the item goes to the false branch, which routes FYI emails to a Notion inbox for batch review and JUNK emails to an archive action.
{{ $json.category }} equals "URGENT"
→ TRUE: Telegram node → send immediate alert
→ FALSE: Switch node → route FYI vs JUNK separately
The false branch here connects to a Switch node, not another IF. That's by design. Once you have three or more distinct outcomes from the same field, Switch handles it better than chaining IFs. More on that in the nested-vs-Switch section.
2. Stripe payment routing by amount
When a Stripe charge webhook fires, I need to do different things depending on the amount. Charges over $200 go through a high-value path: log to a Google Sheet, send a Slack message to the business channel, and add to a follow-up queue. Everything under $200 just logs to the sheet and stops there.
{{ $json.data.object.amount }} greater than 20000
(Stripe amounts are in cents — $200 = 20000)
→ TRUE: Sheets log + Slack alert + follow-up queue
→ FALSE: Sheets log only
Stripe sends amount as an integer in the smallest currency unit. For USD that's cents. $200 arrives as 20000. Not a bug, just something to know before you set the threshold.
3. Field existence gate
This one comes up constantly and it's the pattern I use most for defensive workflow design. Some webhooks and API responses include a field sometimes and skip it other times. If I try to reference {{ $json.customer_id }} downstream when the field doesn't exist on a given item, the workflow errors and stops.
The fix: gate on the field before anything downstream tries to use it.
{{ $json.customer_id }} is not empty
→ TRUE: continue to CRM update node
→ FALSE: log to error sheet, stop processing this item
The condition type to use here is "is not empty" — it catches null, undefined, and empty string in one check. If you use "does not equal empty string" instead, null fields sneak through. Learned that one the hard way when the CRM started getting records with no customer attached.
Filter drops rejects. IF routes them. Use the wrong one and your canvas turns into spaghetti.
This trips people up. Both nodes evaluate conditions. The difference is what they do with items that don't match.
IF node: routes all items to one of two outputs. Every item exits somewhere (true or false). Nothing gets dropped.
Filter node: drops items that don't match. Only items that pass the condition continue. There's no false branch at all. The rejects stop there and never continue downstream.
Use Filter when you're narrowing a set and the rejects don't matter. You pulled 300 rows from a sheet, you only want the ones where status is active, you don't care what happens to the rest. Filter is clean for that. One node, one output, done.
Use IF when both groups need to go somewhere. In the email triage example: URGENT emails need a Telegram alert, non-URGENT emails need a different path. You can't drop the non-URGENT ones. They still need handling. That's an IF node job.
Ask: "Do I need to do something with items that don't match?" If yes, use IF. If no, use Filter. Unnecessary IF branches clutter your canvas and make workflows harder to read at a glance.
Stacked IFs for three-way splits is the workflow smell that Switch node cures
When you have more than two outcomes, chaining IF nodes is tempting. IF node A routes true to IF node B, false goes elsewhere, IF node B routes true to IF node C. You've probably seen this. It works. It also becomes a visual tangle once you have three or four levels, and adding a new case means rebuilding part of the chain.
The Switch node handles multiple outcomes from a single field without any chaining. You define rules directly in the node — each rule maps to a separate output. Items hit whichever rule matches first. There's an optional fallback output for anything that doesn't match.
In my email triage workflow: one IF node handles the URGENT split. Everything else goes to a Switch node with two rules: category equals "FYI" routes to one path, category equals "JUNK" routes to another. If I add a fourth category someday, I add one rule to that Switch node. No rebuilding required.
Chained IFs still make sense for sequential checks — checking whether a field exists first, then checking what value it has if it does. That's genuinely sequential logic and IF nodes are fine for it. But when you're routing one field with multiple possible values to multiple destinations, Switch is the right call every time.
Use nested IF when conditions are sequential: check A, and only if A is true do you then check B.
Use Switch when you're branching on the same field with multiple possible values. Three-plus outcomes on one field should almost always be a Switch.
Four IF node mistakes that cost an hour each, every time they show up
Forgetting the false branch
You add an IF node and wire only the true output. n8n doesn't warn you. Items that hit the false branch vanish silently. The workflow execution shows as successful. You come back an hour later and wonder why forty records didn't make it to the database.
Always wire both outputs. The false branch can go to a No-Op node, a simple error log, a Slack message, anything. It just needs to go somewhere. When a branch has no destination, you have no visibility into what's being dropped and why.
Data type mismatches
This one wastes at least an hour the first time it hits you. You set a condition: {{ $json.amount }} greater than 500. The workflow runs. Zero items go to the true branch, even though you can literally see amount: "750" sitting there in the input data panel.
The problem: amount is a string. "750", not 750. The "greater than" numeric operator won't compare a string to a number correctly. It returns false every time. No error message, just wrong behavior you have to figure out by staring at the data panel.
Two ways to fix it. Add a Set node before the IF and cast the field: set amount to {{ parseInt($json.amount) }}. Now it's an actual integer and the numeric operators work. Or convert it in a Code node before the condition check. Either way, fix it upstream. Don't try to work around a type problem inside the IF node itself.
Using IF where Filter is cleaner
Every time you use an IF node where you don't actually need the false branch, you're adding a dead-end wire to your canvas. Over time this turns a clean workflow into a spaghetti diagram. Audit your IFs occasionally. If the false output goes to nothing or just a No-Op, replace it with a Filter node.
Building three-way splits with chained IFs
Two IF nodes in a chain to split one field into three buckets: it works, but it's harder to read and harder to extend. If you're doing this, check whether a Switch node solves it in one step. Usually it does.
What this looks like in practice
All 14 workflows in the n8n pack use IF nodes for routing. The lead capture workflow gates on company name before pushing to the CRM: no company name, it logs as a consumer lead instead. The email triage workflow uses IF plus Switch to sort three categories into three paths without chaining IFs. The payment hook uses IF to split high-value and standard charges and handle them differently. Each one is simple on its own. The useful part is seeing them in context: how they connect to the nodes before and after, and why the branches land where they do.
The IF node is not complicated. It is binary, intentional, and completely unforgiving about type mismatches. Build it right once, understand the false branch as a first-class citizen, and it becomes one of the most reliable tools in the stack.
See IF routing patterns inside 14 real workflows
For $97 one-time, the n8n Automation Starter Pack gives you 14 annotated workflows showing field gates, category splits, threshold routing, and the exact branch logic behind each decision.
See Google Workspace MCP →One-time $97 · Instant download · 30-day money-back guarantee