IF, Switch, Filter: routing your data in n8n without sending it down the wrong branch
Published 26 July 2026 · 7 min read
A workflow that applies the same treatment to everything coming in isn't worth much: automation gets interesting when each piece of data takes the path that fits it — the urgent email fires a Slack alert, the invoice goes to accounting, the serious lead goes to sales. That conditional routing rests on three n8n core nodes: IF, Switch, and Filter. They look simple, but they concentrate a handful of traps that explain a good share of beginner workflows that "almost work": items routed to the wrong side, unexpected cases disappearing, missing fields flipping everything to false. This guide covers all three nodes, the expressions you'll actually use, and the pattern on the rise: routing based on a classification produced by an LLM.
The IF node: two outputs, but item by item
The IF node evaluates one or more conditions and sends each item to the true or false output. Conditions cover the usual types — string (equals, contains, starts with…), number (larger, smaller…), boolean, date (before, after) — and combine with AND (all must be true) or OR (at least one). A typical test:
{{ $json.amount }}— Number — larger than —500- combined with AND:
{{ $json.country }}— String — equals —FR
The point almost every beginner misses: the IF node doesn't route "the workflow," it routes each item individually. If the node receives 10 items, each is evaluated separately — 7 can go out true and 3 out false, and both branches execute within the same run. There is no global "chosen path." To test a whole-batch condition ("at least one item in error"), first aggregate the items into one, then apply the IF to that result.
The Switch node: multiple outputs, two modes
Once routing goes beyond two cases, chaining IF nodes becomes unreadable. The Switch node defines as many outputs as needed, in one of two modes.
Rules mode: you stack rules evaluated in order — the first one that matches determines the item's output. Example for ticket triage: rule 1 if {{ $json.priority }} equals critical, rule 2 if equals high, rule 3 if equals normal. Each rule can be given an explicit output name (the Rename Output option), which makes the canvas instantly readable.
Expression mode: instead of a list of rules, you write a single expression that returns the output index (0, 1, 2…). For example:
{{ $json.score >= 80 ? 0 : $json.score >= 50 ? 1 : 2 }}
This mode is compact and handy when the logic is computational (score brackets, a modulo to spread load), but less self-documenting than Rules mode — save it for cases where rules would feel artificial.
The Fallback output: always connect it. In Rules mode, the Fallback Output option adds an output that catches items matching none of the rules. Without it, an unexpected item silently disappears — and with an LLM upstream that one day returns Urgent instead of urgent, that happens sooner than you'd think. Connect the fallback at least to a log or a notification: it's your safety net and your best detector of forgotten cases.
The Filter node: dropping items without creating a branch
Sometimes you don't want to route, just keep or discard: only process emails less than 24 hours old, ignore leads without an email address. The Filter node applies the same conditions as an IF but has a single output: items that pass continue, the rest are simply dropped. It's cleaner than an IF whose false output is left dangling — the intent ("this is a filter, not a fork") is explicit on the canvas.
The expressions you'll use every day
The heart of a condition is its expression. A few patterns worth knowing (covered in depth in our guide to JavaScript expressions in n8n):
{{ $json.field }}: the value of a field on the current item;{{ $json.subject.toLowerCase().includes("invoice") }}: a case-insensitive substring test, very useful as a boolean "is true" condition;{{ $json.email.endsWith("@gmail.com") }}: spotting personal addresses in lead qualification;{{ $json.amount > 1000 && $json.currency === "EUR" }}: combining directly inside one expression rather than through multiple rules.
Trap number one: missing or null fields. If $json.client.country is tested while client doesn't exist on some items, the expression fails or returns undefined — and the item goes out the false side with no error to warn you. Two safeguards: the optional chaining operator {{ $json.client?.country }}, and the default fallback {{ $json.status ?? "unknown" }}, which gives incomplete data an explicit value. In a Switch, you can even dedicate the first rule to empty fields, so those items are handled separately instead of contaminating the other branches.
After routing: recombining with Merge
Routing is often only half the job: after differentiated processing, branches frequently need to converge again — to write to the same table, or send the same recap. That's the Merge node's job, with its Append mode (stacking a Switch's outputs) or Combine mode (re-pairing by identifier), covered in detail in our complete guide to the Merge node. The Switch → processing → Merge combo is probably the most common structural motif in non-trivial n8n workflows.
The pattern on the rise: routing based on an AI classification
Hand-written conditions quickly hit their limits on free text: there's no listing every keyword that makes an email "urgent." The modern pattern: an LLM classifies the element (category, urgency, language, intent) as structured output — reliable JSON produced through the Structured Output Parser — then a Switch in Rules mode routes on the returned field: {{ $json.category }} equals support, sales, billing… That's exactly the architecture of our AI email triage workflow over IMAP, and the same blueprint powers inbound lead qualification as well as AI-driven support ticket scoring: the LLM produces the criterion, the Switch executes the routing. Two hygiene rules: constrain the LLM to a closed set of values (an enum in the output schema), and connect the Switch's Fallback anyway — a model always steps outside the lines eventually.
Keeping a human in the loop on ambiguous cases
When the classifier is an LLM, not all decisions carry the same weight: classifying a newsletter is harmless, routing a customer complaint to the wrong team — or automatically triggering a refund — much less so. The good practice is to explicitly plan an "uncertain" branch: ask the model for a confidence level or an uncertain category in its structured output, and route those items to manual review — for example through a human approval step with the Wait node and Slack — rather than forcing an automatic decision.
This isn't timidity: it's a classic finding of human factors research. The reference study by Parasuraman and Riley, "Humans and Automation: Use, Misuse, Disuse, Abuse," published in Human Factors in 1997, shows that automation failures rarely come from the technology alone but from how it's used: overtrust (misuse) pushes people to delegate decisions to the machine that deserve human review, while undertrust (disuse) leads them to ignore automation that is actually reliable. Applied to our workflows: automate the clear-cut cases all the way, and own a human branch for the ambiguous ones — that exact balance is what makes automation sustainable, because the team can trust it without being at its mercy.
Good practices for routing that stays maintainable
- Name your branches. Rename the Switch outputs (Rename Output) and the nodes themselves: "Switch — email category" still reads well in six months, "Switch1" doesn't.
- Test every path. Pin sample data (pin data) on the upstream node and check that one item of each category — including a deliberately malformed one — lands on the right output before going to production.
- Fallback connected, always. Even to a plain log. An item lost in silence is the worst bug to diagnose.
- Secure the fields you test.
?.and??in expressions, or a dedicated rule for incomplete data. - IF for two cases, Switch beyond that. Three cascading IFs do the job of one Switch, at a third of the readability.
Going further
IF, Switch, and Filter form — together with Merge — the basic grammar of any n8n workflow that goes beyond a straight line, and the entry step toward AI automations, where routing relies on a model's classification rather than hard-coded keywords. If you're just getting started with that part, our guide to getting started with AI nodes in n8n lays the foundations; and FlowKit's workflows, like the AI email triage, apply exactly the patterns from this article — named Switches, connected Fallback, human review branch included — ready to import and adapt to your context.
FAQ
Frequently asked questions
What's the difference between the IF node and the Switch node in n8n?
The IF node evaluates one or more combined conditions and offers only two outputs: true and false. The Switch node lets you define as many outputs as you need, either through a list of rules evaluated in order (Rules mode) or through an expression that directly returns the output index (Expression mode). As soon as routing goes beyond two cases — for example routing by a category returned by an LLM — Switch is the right choice; IF stays more readable for a simple binary test.
Does the IF node route the whole workflow or each item separately?
Each item separately: that's the classic beginner trap. If 10 items reach an IF node, each one is evaluated individually — 7 can go out the true output and 3 out the false output, and both branches execute. The IF node doesn't pick a single path for the entire run; it sorts items one by one. If you want a global decision (for example 'if at least one item is urgent'), you first need to aggregate the items, then test the result.
What is the Switch node's Fallback output for, and should you connect it?
In Rules mode, the Fallback output (the Fallback Output option) catches items that match none of the rules. Yes, you should always connect it: without it, an unexpected item — a category misspelled by an LLM, a brand-new value — silently vanishes from the workflow. Connect it at least to a notification or a log; it's often where you discover the cases you hadn't anticipated.
How do you avoid errors when a tested field is missing or null?
A missing field evaluates to undefined, and a comparison on it can route the item to the wrong side with no visible error. The simplest safeguard is to secure the expression with a fallback: {{ $json.status ?? 'unknown' }} or the optional chaining operator {{ $json.client?.country }}. You can also add a dedicated first 'empty field' rule in a Switch, to handle incomplete data explicitly instead of letting it pollute the other branches.
Bundle FlowKit Complet
€269