# Reuse a set of nodes as a group

Turn a few steps into one reusable card with named ways in and exits, drop it into any flow, and change it in one place.

Updated 23 September 2026

## What a group is

A **group** is a set of steps you build once and use in as many flows as you like. In a flow it is a single card. Change the group and every flow that uses it changes too, so a check you rely on in three commands lives in one place.

The card reads like any other step. Its **ways in** are on the left, its named **exits** are on the right — **Allowed**, **Refused**, **Full** — and every group also has an **error** exit. The values it hands out sit in its tray, under the name of the exit that hands them out, ready for a [data wire](https://bloopbot.com/docs/data-wires).

![A group card named Queue has room, with an Open button, the exits Full, Room and Error on its right and, under the exit headings FULL and ROOM, a queueLength value each. A Chat command leads into it, Full and Room lead to two Chat messages, and dashed wires carry queueLength into both messages.](https://bloopbot.com/docs/screenshots/group-node-card.jpg)

_A group called Queue has room in a flow: one way in from the Chat command, the Full and Room exits leading to two Chat messages, and the queueLength value it hands out wired into both._

## Before you start

- Groups are built on the **Canvas**. A flow that uses a group can still open in the Simple view: the group’s inputs show as fields there.
- Anyone who can edit your flows can make and edit groups, including your editors.
- Groups belong to your channel. A group that came with a template is your own copy, and changing it never touches anyone else’s.

## 1. Turn a selection into a group

1. Open a flow on the **Canvas** and select the steps you want to reuse: click the first one, then Shift-click the others (Ctrl-click on Windows and ⌘-click on a Mac work too).
2. In the panel on the right, press **Group selection**. It tells you how many ways in and exits the group will have.
3. Give the group a name, such as `Queue has room`.
4. Name each exit for what it means. They start with the label of the connection they replace, such as `true` and `false` — with the step’s name in front when two connections share a label, such as `Check a value true`; `Full` and `Room` read better on the card.
5. Press **Save as group**.

![Two selected steps on the canvas, Check song request limits and Check a value, and the Selection panel reading 2 nodes selected: Group name is Queue has room, the Exits list renames true to Full and false to Room, each marked “hands out queueLength”, above Save as group and Cancel.](https://bloopbot.com/docs/screenshots/group-selection.jpg)

_Two steps selected and Group selection open: the group is named Queue has room, and its two exits are renamed Full and Room. Each says which value it hands out._

The group is saved, and the steps you selected are replaced by one group card wired exactly as they were. Every connection that came into the selection becomes a way in, every connection that left it becomes an exit, a value the selection read from an earlier step becomes an input on the card, and a value a later step read from the selection becomes a value its exit hands out. **Undo** puts the steps back in one go, and removes the group it had just made when nothing else uses it yet.

When a selection cannot become a group — a trigger is in it, for example — **Group selection** is greyed out and the reason is written under it.

## 2. Open a group and find your groups

Double-click a group card, or press **Open** on it, to edit what is inside. The canvas swaps to the group’s own steps, and the top bar shows where you are: the flow’s name, then the group. Click the flow’s name to go back to it.

A bar across the top of the group says who uses it — for example _“Used by 1 flow: Is there room? — saving recompiles it.”_ — so you know what a change reaches before you make it. With nothing selected, the panel on the right shows the group’s **Ways in** and **Exits**, as its card shows them in every flow.

![The editor open inside the group Queue has room: the top bar reads Is there room? › Group › Queue has room with History, Test the flow and Save; a bar reads “Used by 1 flow: Is there room? — saving recompiles it.”; the canvas runs from a Group input through Check song request limits and Check a value to two Group outputs, Full and Room; the panel lists Ways in (in, no inputs) and Exits (Full and Room, each handing out queueLength).](https://bloopbot.com/docs/screenshots/group-body.jpg)

_Inside Queue has room: the path back to Is there room? at the top left, the Used by bar, the steps from Group input to the two Group outputs, and the Ways in and Exits panel._

Every group your channel has is on the **Flows** page, under the **Groups** tab beside **Flows**. Each row says which flows use it and how many ways in and exits it has. **+ New group** starts an empty one; **Open** edits one, even a group no flow uses yet.

![The Groups tab of the Flows page, reading Groups · 2, with the line “A group is a set of nodes you build once and drop into any flow.” and a + New group button, above two rows: Enforce chat rules, used by AI chat moderation — delete and warn, 1 way in and 2 exits; Screen reward text, used by Check reward text before it goes on stream, 1 way in and 2 exits. Each row has Open and Delete.](https://bloopbot.com/docs/screenshots/groups-tab.jpg)

_The Groups tab: Enforce chat rules is used by AI chat moderation — delete and warn and has 1 way in and 2 exits; Screen reward text is used by Check reward text before it goes on stream and has 1 way in and 2 exits._

## 3. Ways in, inputs, exits and values

Inside a group, a **Group input** step is a way in and a **Group output** step is an exit. Select one to edit it in the panel.

| Part | On the group card | Inside the group |
| --- | --- | --- |
| Way in | A connection point on the left, named after the way in | A **Group input** step the run starts from |
| Input | A field under its way in: type a value, pick one from **Insert…** or drop a data wire on it | Read as `$(input.name)`; the default you give it is used when the card leaves the field blank |
| Exit | A named connection point on the right | A **Group output** step; a path that reaches one leaves the group by that exit |
| Exit value | A port under the exit’s name, read as `$(from.group.name)` | Filled in on each **Group output** step for that exit |

An input is text, a number or a yes/no. A value belongs to its exit: a group that finds a track can hand out its **Spotify link** and **Song title** on an **Allowed** exit — read as `$(from.checks.uri)` and `$(from.checks.title)` — and a refusal line on a **Refused** exit, so a step after **Refused** has no track to read. **Insert…** only offers a step the values the exit before it hands out.

A group can have more than one way in, each with its own inputs, and each call through a way in starts fresh. Use **+ Add a way in** and **+ Add an exit** in the panel, and the arrows beside each to set the order they appear on the card. Renaming an input keeps every flow’s value for it.

## 4. Use a group in another flow

1. Open the other flow on the **Canvas**.
2. Press **Browse the library** and choose the **Groups** topic, or type the group’s name into **Add a node** (⌘K). Your groups also turn up under **Suggested next**.
3. Add it, connect the step before it to a way in, and connect each exit you use to what should happen next.
4. Fill in its inputs on the card, and wire its exit values into the steps after it.
5. Press **Save**.

![The node library with the Groups topic selected: no triggers or conditions, and two actions — Enforce chat rules, your group used by 1 flow, leaving by Handled or Clean, and Screen reward text, your group used by 1 flow, leaving by Safe or Refunded.](https://bloopbot.com/docs/screenshots/node-library-groups.jpg)

_The node library’s Groups topic, listing Enforce chat rules and Screen reward text with what each one leaves by._

## 5. Save, test and undo a group

Saving a group saves it for every flow that uses it, and checks each of those flows first. If the change would break one — an exit a flow depends on is gone, or a value it reads is no longer handed out — nothing is saved and a bar says **Not saved.** and names the flow and the problem. Fix the group, or fix that flow first, and save again.

**Test the flow**, in the group’s top bar, runs the flow you opened the group from as a simulated test: the steps inside the group light up as the run passes through them, and the Run log lists them under the group’s line. A simulated test does not send a Twitch message. **History** lists the group’s earlier versions; restoring one goes through the same check as a save.

## What reaches inside a group

- **The trigger passes through.** Inside a group, `$(user)`, `$(query)` and the rest of the trigger’s words still work, and actions still act for the viewer who started the flow.
- **Steps outside do not.** A step inside cannot read a step in the flow around it: anything it needs comes in as an input. A value a step keeps for the rest of the run — a Web request’s **Keep the answer as**, read as `$(local.…)` — stays on its own side: a group neither sees the flow’s nor leaves its own behind.
- **Each place a group sits has its own cooldown.** A Cooldown inside a group counts per card: two cards of the same group in one flow keep separate cooldowns, and a flow with the group in it once has one, whichever way in the run took.
- **Errors leave by the error exit.** A step inside that fails with nothing connected to its own **error** sends the run out of the group’s **error** exit. A Stop step inside ends the whole run.
- **Limits are the flow’s.** Steps inside a group count towards the flow’s own limits on steps, time and chat messages. Groups can sit inside groups, three deep at most.

## Groups that come with templates

Two templates ship with their checks already in a group. Installing the template installs the group as your own copy; open it from the flow to read or change it.

| Group | Used by | Exits |
| --- | --- | --- |
| **Enforce chat rules** | AI chat moderation — delete and warn | **Handled**, **Clean** |
| **Screen reward text** | Check reward text before it goes on stream | **Safe**, **Refunded** |

[Set up AI moderation](https://bloopbot.com/docs/ai-moderation) and [Screen viewer text with AI](https://bloopbot.com/docs/text-screening) walk through what each one decides. A group of your own can hand values out of an exit: the card shows each value by its name in words, and a step reads it by the short name beside it, such as `$(from.checks.uri)`. Renaming a value in your copy changes the words on the card; the short name, and every step that reads it, stays as it is.

An earlier **Song request** or **Smart !sr** used a third group, **Song request checks**. A copy you installed keeps it and still works; the current templates make one **Can they request a song?** check instead.

## Delete a group

On the **Groups** tab, press **Delete** on the group’s row. A group that is still in use is not deleted: the message names every flow that uses it, including a flow waiting in the Trash, marked _(in the Trash)_ — restoring that flow would otherwise bring back a flow with a missing group.

1. Open each flow the message names and remove the group card, or replace it with the steps it stood for.
2. For a flow marked _(in the Trash)_, open the Trash and press **Delete forever** on it, or restore it and remove the group.
3. Delete the group again.

## A worked example: Queue has room

A check that answers “is there room in the song queue?”, reused by any command that needs it.

1. Build a flow `!room`: **Chat command** → **Check song request limits** → **Check a value** with **Check** set to `$(from.queue.queueLength)`, **is at least** and `10`. Wire **true** to a Chat message `The queue is full ($(from.queue.queueLength) songs). Try again later!` and **false** to `There is room: $(from.queue.queueLength) songs waiting.`
2. Select **Check song request limits** and the **Check a value** step together and press **Group selection**.
3. Name it `Queue has room`, rename the exits `Full` and `Room`, and press **Save as group**.
4. Press **Save** on the flow, then **Test fire** and read the Run log.
5. In another command, add **Queue has room** from the library’s **Groups** topic and connect its **Room** exit to whatever should happen when there is space.

## What you should see

- One **Queue has room** card where the two steps were, with **Full**, **Room** and **error** exits and the two messages still wired to them.
- Both messages’ numbers wired from the `queueLength` value under the card’s exit.
- On the **Groups** tab: _Used by Is there room?_, and after the second command, both flows.
- In chat, with twelve songs waiting: `The queue is full (12 songs). Try again later!`

## Troubleshooting

- **“Find the track needs $(query); the On a timer trigger doesn’t provide it.”** The group reads a word the trigger does not have. Fill the group’s input on the card instead — for example the song from a variable — or use the group under a trigger that has it, such as a Chat command.
- **“$(from.checks.title) is always empty here: no exit that hands out “title” leads here.”** The step reads a value from an exit it does not come after. Move it after the exit that hands the value out, or read a value the exit before it does have.
- **“… is empty on some ways here.”** A warning, not an error: some paths reach the step through an exit without that value, and there it is empty. Split the step per exit if the empty case matters.
- **Not saved.** on a group: the change would break a flow that uses it. The bar names the flow; change the flow first, or keep what it depends on.
- **A path ends without a Group output.** Saving a group warns about it: a flow using the group stops there. Connect that path to an exit if the flow should carry on.
- **“Group input only works inside a group.”** Group input and Group output steps belong inside a group. Use **Group selection** to make one.
- **The group will not delete.** It is still used — see [Delete a group](https://bloopbot.com/docs/group-nodes#delete).
