Ultra Tools Textbox
Up to 32 text boxes in one node, and you can still read them all
- string
- string_2
- string_3
- string_4
- string_5
- string_6
- string_7
- string_8
- string_9
- string_10
- string_11
- string_12
- string_13
- string_14
- string_15
- string_16
- string_17
- string_18
- string_19
- string_20
- string_21
- string_22
- string_23
- string_24
- string_25
- string_26
- string_27
- string_28
- string_29
- string_30
- string_31
- string_32
A text node that fixes the annoying part
ComfyUI's core PrimitiveString and String Multiline nodes do one thing each. So you end up with five of them scattered around the canvas: positive prompt here, negative prompt there, a filename prefix over by the save node, a note to yourself about why the CFG is 1.0, and something fed in from a wildcard node you want to eyeball. They're not related, they're just all strings.
Ultra Tools Textbox puts them in one node, in rows. Up to 32. Each row is an independent IN N → OUT N pair, and - this is the part that makes it worth installing - connecting an input does not turn the row into a grey read-only slab. After a run, the value the node actually received is displayed in the row, so you can look at it, copy it, and see what your prompt generator really produced instead of trusting it.
If you've ever wired a string into a core text widget and then wondered what came out the other side, that's the feature you're buying.
How it works
The node declares text as a required multiline STRING and source through source_32 as optional STRING inputs, and returns string through string_32. That's a fixed 32-wide interface; the frontend simply shows you as many rows as you've added, and hides the rest.
Mechanically that's a wrapper around the same "convert widget to input" pattern ComfyUI itself uses - an optional input that's None when unconnected, in which case the widget value is used. The difference is that it's all of them, in one node, with a UI that keeps typed and received values legible.
The extras are all frontend conveniences, but they're the kind that change how you work:
- + Add text grows the node a row at a time;
×deletes a middle row and keeps the later rows' connections intact. - ⋀ ⋁ reorders: text, row name and the wire attached to it move together.
- Renaming a row ("subject", "style", "filename") renames the output socket too, so the graph is readable three weeks later.
- Each row has a Copy button.
- A string that happens to look like JSON is stored and treated as plain text, not parsed.
Row count and contents live in the workflow, so it survives saving and reloading - no external config file, no surprises on another machine.
What you set
Honestly? text and nothing else, then grow it.
- text - the first row's value. Required, multiline, defaults to empty.
- source … source_32 - optional inputs. Wire a prompt node into
sourceand its output appears in row 1 after execution, editable and copyable. - string … string_32 - outputs.
stringis the first row, and so on down.
The one trap is the fixed width: all 32 outputs exist whether or not you added rows, so an unused slot outputs an empty string, and a downstream node wired to the wrong slot gets "" rather than an error. Name your rows before you start wiring and this stops being a problem.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Yamoon-jjang/comfyui-Ultra-Tools
Or Manager → Ultra Tools. Restart, Ctrl+F5 - the whole node's UI is web/ultra_textbox.js, so a hard refresh after install or update is not optional.
requirements.txt in this pack installs nothing: torch, Pillow, numpy, aiohttp, stdlib. Nothing here touches a model or the network.
If you installed the old standalone comfyui-Ultra-textbox, delete that folder before this one. Same class name, two copies, and a "node not found" mystery.
Where it bites
Editing a row that has an input connected. The input wins at run time - that's what an input means. Your typed text is what you get until something is wired in; after a run the received value is shown and replaces what you can see. If you want to keep a hand-written fallback, leave the input unconnected and copy values in manually.
Very large rows. These are plain multiline widgets; a 6,000-token prompt in a row is unwieldy to scroll and the node is meant as a workspace, not a scratchpad. Keep long boilerplate in a file and read it in if that's your workflow.
It is not a primitive for automation. If you need a value a user sets once at the top of the graph and reuses everywhere, the core primitive nodes (or an rgthree Context) are the plumbing answer. This node is for organising prompts you're actively editing - and on that job it's better than anything in core.
Inputs (33)
| Name | Type | Default | Description |
|---|---|---|---|
| text | STRING | — | |
| sourceopt | STRING | — | |
| source_2opt | STRING | — | |
| source_3opt | STRING | — | |
| source_4opt | STRING | — | |
| source_5opt | STRING | — | |
| source_6opt | STRING | — | |
| source_7opt | STRING | — | |
| source_8opt | STRING | — | |
| source_9opt | STRING | — | |
| source_10opt | STRING | — | |
| source_11opt | STRING | — | |
| source_12opt | STRING | — | |
| source_13opt | STRING | — | |
| source_14opt | STRING | — | |
| source_15opt | STRING | — | |
| source_16opt | STRING | — | |
| source_17opt | STRING | — | |
| source_18opt | STRING | — | |
| source_19opt | STRING | — | |
| source_20opt | STRING | — | |
| source_21opt | STRING | — | |
| source_22opt | STRING | — | |
| source_23opt | STRING | — | |
| source_24opt | STRING | — | |
| source_25opt | STRING | — | |
| source_26opt | STRING | — | |
| source_27opt | STRING | — | |
| source_28opt | STRING | — | |
| source_29opt | STRING | — | |
| source_30opt | STRING | — | |
| source_31opt | STRING | — | |
| source_32opt | STRING | — |
Outputs (32)
| Name | Type | Description |
|---|---|---|
| string | STRING | — |
| string_2 | STRING | — |
| string_3 | STRING | — |
| string_4 | STRING | — |
| string_5 | STRING | — |
| string_6 | STRING | — |
| string_7 | STRING | — |
| string_8 | STRING | — |
| string_9 | STRING | — |
| string_10 | STRING | — |
| string_11 | STRING | — |
| string_12 | STRING | — |
| string_13 | STRING | — |
| string_14 | STRING | — |
| string_15 | STRING | — |
| string_16 | STRING | — |
| string_17 | STRING | — |
| string_18 | STRING | — |
| string_19 | STRING | — |
| string_20 | STRING | — |
| string_21 | STRING | — |
| string_22 | STRING | — |
| string_23 | STRING | — |
| string_24 | STRING | — |
| string_25 | STRING | — |
| string_26 | STRING | — |
| string_27 | STRING | — |
| string_28 | STRING | — |
| string_29 | STRING | — |
| string_30 | STRING | — |
| string_31 | STRING | — |
| string_32 | STRING | — |