Node-RED subflows let you package a group of nodes into a single reusable node, so logic you wrote once can be dropped onto any tab and configured per use. In this explainer you will build an MQTT alert subflow with its own settings, add status reporting and error handling, and reuse it across several ConnectMQ topics without copying a single function.
The Problem Subflows Solve
MQTT flows tend to repeat themselves. Every sensor topic needs the same steps: parse the payload, check it is sane, compare a value with a threshold, publish an alert, maybe rate-limit the alert so an operator is not flooded. The first time, you build these nodes by hand. The fifth time, you copy and paste them. By the twentieth, a bug fix means editing twenty function nodes, and one of them always gets missed.
A subflow fixes that. You define the logic once as a template, and every place you use it is an instance of that template. Change the template, and every instance updates on the next deploy. Each instance can still have its own settings, such as a different threshold or alert topic, through subflow properties.
| Approach | Reuse | Per-use settings | Maintenance |
|---|---|---|---|
| Copy and paste nodes | Manual | Edit each copy | Every copy must be fixed |
| Link nodes to one shared chain | Shared, single instance | Must be carried in msg |
One place, but shared state can clash |
| Subflow | Each use is an instance | Properties per instance | One template, isolated instance state |
Link nodes are a good option when you want one shared processing chain. Node-RED subflows are better when each use needs its own configuration and its own state.
How Node-RED Subflows Work
A subflow has a template and instances:
- Template. Opened as its own tab in the editor, it contains the nodes, an optional input port (0 or 1), any number of output ports and an optional status port.
- Instances. These appear in the palette under "subflows" and behave like any other node. Each instance has its own flow context and its own values for the subflow properties.
- Properties. Defined on the template's edit dialog, each with a name, type and default. Inside the subflow, nodes read them as environment variables:
env.get("THRESHOLD")in a function, or${THRESHOLD}typed as the whole value of a node's text field.
You can create a subflow from scratch through the menu (Subflows → Create Subflow) or, more often, select existing nodes and choose Subflows → Selection to Subflow. Node-RED wires the inputs and outputs for you based on which wires crossed the selection boundary.
Build an MQTT Alert Subflow
The goal: one node that takes a parsed MQTT telemetry message, checks a field against a threshold, passes normal data on output 1 and emits an alert message on output 2, with a cool-down so the same device does not alert every second.
Step 1: Create the template and properties
Create a new subflow, name it "Threshold alert", and set inputs to 1 and outputs to 2. Open the subflow's properties and add:
| Property | Type | Default | Meaning |
|---|---|---|---|
FIELD |
string | temp |
Payload field to check |
THRESHOLD |
number | 30 |
Alert above this value |
COOLDOWN_S |
number | 60 |
Minimum seconds between alerts per topic |
SEVERITY |
string | high |
Label included in the alert |
You can also give each property a UI widget (text, number, checkbox, select), which makes instances friendlier for teammates who never open the template.
Step 2: Add the logic
Inside the template, wire input → function → two outputs. The function:
// Function inside the "Threshold alert" subflow, 2 outputs
const field = env.get("FIELD");
const threshold = Number(env.get("THRESHOLD"));
const cooldownMs = Number(env.get("COOLDOWN_S")) * 1000;
const value = msg.payload && msg.payload[field];
if (typeof value !== "number") {
node.error(`Field "${field}" missing or not numeric on ${msg.topic}`, msg);
return [null, null];
}
let alertMsg = null;
if (value > threshold) {
const lastAlerts = flow.get("lastAlerts") || {};
const now = Date.now();
if (!lastAlerts[msg.topic] || now - lastAlerts[msg.topic] > cooldownMs) {
lastAlerts[msg.topic] = now;
flow.set("lastAlerts", lastAlerts);
alertMsg = {
topic: msg.topic.replace(/\/telemetry$/, "/alert"),
payload: {
field: field,
value: value,
threshold: threshold,
severity: env.get("SEVERITY"),
ts: new Date(now).toISOString()
}
};
}
}
node.status({ fill: alertMsg ? "red" : "green", shape: "dot", text: `${field}=${value}` });
return [msg, alertMsg];
Note that flow here is the instance's own context. Two instances of the subflow do not share their cool-down tables, which is exactly what you want.
Step 3: Report status on the instance
Enable the subflow's status port and wire a Status node (scoped to the function) into it. The instance node on your main tab then shows the same green or red dot and the last value, so you can monitor many instances at a glance.
Step 4: Use it on several topics
On your main tab, add an mqtt in node for each source, or one with a wildcard, and wire them into separate instances with different settings. For a quick test, publish to the public MQTT test broker, replacing e2a7 with your own random string:
mosquitto_pub -h broker.connectmq.com -p 1883 -i demo-sub-e2a7 \
-t "connectmq/demo/e2a7/furnace1/telemetry" -m '{"temp": 912}'
mosquitto_pub -h broker.connectmq.com -p 1883 -i demo-sub-e2a7 \
-t "connectmq/demo/e2a7/tank1/telemetry" -m '{"level": 97}'
Instance one checks FIELD=temp with THRESHOLD=850 for furnaces; instance two checks FIELD=level with THRESHOLD=95 for tanks. Wire each instance's second output to one shared mqtt out node with an empty topic so alerts publish to the topic the function set.
A Subflow That Contains Its Own MQTT Subscription
A subflow does not need an input. A subflow with zero inputs can hold its own mqtt in node whose topic is ${TOPIC}, a subflow property. Each instance then subscribes to a different topic while sharing one broker configuration node, so all instances use the same single connection to the broker.
This is tidy for dashboards with many identical machine panels: one subflow instance per machine, each with TOPIC and a display name. The trade-off is that the subscription is fixed at deploy time. If you need to change topics on the fly, combine subflows with Node-RED dynamic MQTT subscribe instead.
Keep the broker configuration node outside the subflow, on the main flow or global scope. Putting a broker config inside the template would create one broker connection per instance, each needing its own client ID.
Error Handling Inside Subflows
Errors raised inside an instance, including the node.error(..., msg) call above, can be caught by a Catch node inside the subflow template. If no Catch node inside handles them, they propagate to Catch nodes on the tab that contains the instance. A common pattern is to handle known, recoverable errors inside the subflow and let unexpected ones bubble up to a single "errors" handler per tab.
Node-RED Subflows Checklist
- Give every property a sensible default so a new instance works immediately.
- Prefer properties to hard-coded values; anything that differs between uses belongs in a property.
- Keep subflows focused: one job each, such as "parse", "alert" or "rate limit".
- Add a status port so instances are observable on the canvas.
- Keep broker configuration nodes outside the template.
- Document the subflow in its description field; it appears in the help sidebar for every instance.
- Version your templates with git using Node-RED projects, so a template change is reviewed like code.
For the coding side, our Node-RED function node guide covers multi-output returns and async sends in more depth, and the Node-RED context explainer explains how instance flow context relates to its parent through $parent. The official subflow documentation on nodered.org lists every template option.
Run Reusable Node-RED Logic on ConnectMQ
Subflows make it easy to scale from three sensors to three hundred, and your broker should scale with them. ConnectMQ is a managed MQTT broker with TLS, MQTT 5 and a portal where you can set threshold alert rules and watch the alert topics your subflows publish. When you outgrow the free tier, paid Serverless plans support up to 1,000 connections, and Dedicated clusters isolate your traffic entirely. Create your first broker at app.connectmq.com on the free Serverless plan, no credit card needed. If you are still choosing infrastructure, read our guide to the cloud MQTT broker model, and for an end-to-end introduction see the Node-RED MQTT tutorial.
Frequently Asked Questions
What is the difference between a subflow and a group in Node-RED?
A group is a visual box around nodes on one tab. A subflow is a reusable template that appears as a node in the palette and can be instantiated many times with different settings.
How do I pass settings into a Node-RED subflow?
Define properties on the subflow template, then set values on each instance. Inside the subflow, read them with env.get("NAME") or by entering ${NAME} in a node property.
Does each subflow instance have its own context?
Yes. Flow context inside a subflow belongs to that instance, so instances do not interfere. Use the $parent. prefix to reach the containing flow's context.
Do MQTT nodes inside subflow instances open extra broker connections?
Not if they all use the same broker configuration node defined outside the subflow. Node-RED shares one connection per broker configuration node.



