Optional String Join (10)
Ten optional strings, one clean prompt — no empty slots
- text
Optional String Join (10) is the biggest node in ruminar's String Join Tools pack, and it exists for one reason: real prompt assembly graphs have a lot of optional strings, and most of them come up empty some of the time.
Here's the shape of the problem. You've got ten different upstream sources - wildcard pickers, LoRA trigger nodes, per-epoch settings, a hardcoded quality block, a subject, a scene, lighting, camera language. Any of them can be unconnected, bypassed, or produce an empty string on a given run. A naive concatenate node would turn that into a prompt full of ", , ," garbage. This node turns it into a single clean string with the separator only between the pieces that actually showed up.
The behavior that makes it work
Every one of the ten text sockets is optional and wire-only. Unconnected inputs are ignored. Inputs that arrive as an exact empty string are ignored. Inputs that go missing because an upstream node was bypassed are also accepted - this pack was written to treat a bypassed source the same as an unconnected socket, which is a genuinely nicer failure mode than most concatenators give you. Whitespace-only strings are kept (they're not empty), embedded line breaks survive, and the separator is inserted strictly between usable strings. If every input is missing or empty, you get "" back, which is the correct thing to feed a CLIP encoder.
The separator field decodes a few escapes: \n for a line feed, \r\n for CRLF, \t for a tab, \\ for one backslash. Type \\n if you genuinely want the two characters \n. Unsupported sequences like \u or \x are passed through untouched.
Wiring it in
You only set two kinds of things:
separator- the one widget, default", ". Set it once and mostly forget it.text_1throughtext_10- optionalSTRINGsockets. Feed each from whatever string source you have; leave the ones you don't need empty. Output is a singletextSTRING, which you run into your positive CLIP Text Encode (or a negative, or a saver's metadata, or another join).
Because every input is optional, a 10-socket node doesn't force you to fill ten sockets. You can use three and ignore the rest - the unused ones simply never appear in the output.
When ten isn't enough
The README is explicit here: "For more inputs, connect join nodes in stages." Take the text output of this node into the text_1 slot of another join, keep feeding other sources into the remaining slots, and you can compose prompts from any number of branches. The node doesn't care; it just filters and joins.
Installing it
ComfyUI Manager: search "String Join Tools" or "ComfyUI-StringJoinTools" and install. Or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/ruminar/ComfyUI-StringJoinTools
Restart ComfyUI and refresh the browser. No extra Python packages, no model files to download - this whole pack is pure string plumbing, which makes it about the safest custom node install you can do. Find it under String Join Tools.
The honest caveat
For a beginner this node can feel like overkill - most prompts start as one text box. Where it genuinely earns its keep is the moment you stop typing prompts and start composing them from interchangeable parts, especially once a wildcard or random-choice node is in the mix. If you're not there yet, the 2/3/5 variants do the same job with less canvas. When you are, ten optional slots with empty-string forgiveness is exactly the tool you'll want.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| separator | STRING | , | Inserted only between usable strings. Supports \n, \r\n, \t, and \\. |
| text_1opt | STRING | Optional STRING input. Missing inputs and exact empty strings are ignored. | |
| text_2opt | STRING | Optional STRING input. Missing inputs and exact empty strings are ignored. | |
| text_3opt | STRING | Optional STRING input. Missing inputs and exact empty strings are ignored. | |
| text_4opt | STRING | Optional STRING input. Missing inputs and exact empty strings are ignored. | |
| text_5opt | STRING | Optional STRING input. Missing inputs and exact empty strings are ignored. | |
| text_6opt | STRING | Optional STRING input. Missing inputs and exact empty strings are ignored. | |
| text_7opt | STRING | Optional STRING input. Missing inputs and exact empty strings are ignored. | |
| text_8opt | STRING | Optional STRING input. Missing inputs and exact empty strings are ignored. | |
| text_9opt | STRING | Optional STRING input. Missing inputs and exact empty strings are ignored. | |
| text_10opt | STRING | Optional STRING input. Missing inputs and exact empty strings are ignored. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| text | STRING | — |