Format Text
Name your prompt slots instead of counting a, b, c
- values
- STRING
The problem this actually solves
You want your prompt assembled from parts: a quality block, a character block, a lighting line you swap per shot, each in its own text box, merged into one clean string for CLIP Text Encode. ComfyUI's built-in answers to that are bad. Conditioning Concat merges conditioning, not text, and it visibly changes what the encoder sees. A positional format node gives you {0} {1} {2} against slots that renumber the moment you rewire anything - the template still runs, it just prints the lighting text where your subject should be.
Format Text is the fix. It's one of exactly two nodes in ComfyUI-Friendly-String, a tiny pack by ZeronoFreya, and its whole pitch is that fields are bound by name, so the template doesn't care what order your inputs are in. Reorder the graph and the output is identical.
How it works
Two kinds of inputs, two different jobs.
The slots are an auto-growing group (values), labeled a through z - literally string.ascii_lowercase in the source, so the ceiling is 26 text inputs. Plug a plain text node into one and its text is used as-is, under the slot letter.
The interesting part is what happens when a slot's string starts with {. The node tries to parse it as JSON, and if the object has a value field it uses that - and if it also carries __fn_key__, it registers the value under that name. That's the handshake with the pack's other node, Named Text (Multiline), which emits exactly {"__fn_key__": "...", "value": "..."}. So a Named Text box labeled quality plugged into slot a makes {quality} resolvable. Slots are delivery vehicles; names are the interface.
The template (f_string, shown as Format Template) is a multiline box where you write something like:
{quality}, {character}, {lighting}, absurdres
Replacement is a regex over {...}. Two behaviours from the code:
- An unknown or typo'd name becomes an empty string. No error, no red node. Silently blank.
\{and\}are protected first and survive as literal braces, and the result is.strip()ed.
Then Clean Delimiters (on by default) does a cleanup pass: any run of your delimiter with commas/whitespace between is collapsed, so a dead slot doesn't leave masterpiece, , , 1girl behind. Custom Delimiters defaults to , and is the character that pass looks for.
There's also a template-file feature. The first, unlabeled widget is load_text, and the pack's JavaScript replaces it with a file picker plus a dropdown of whatever files you've loaded. Pick a .txt and its contents load into the Format Template box - or, if you leave {LT} in the template, get injected at that marker. The author's priority order: typed input beats the template file, which beats an empty string.
What to wire where
One output: STRING. Straight into CLIP Text Encode (Prompt), or into a negative encoder, or a Save Image filename prefix, or another Format Text node if you're nesting. The only setup you actually have to think about is naming: put a Named Text node in a slot, give it a name, reference that name in the template. Everything else has sane defaults.
Install
No dependencies at all. No requirements.txt, no pip step, no model downloads - the pack's pyproject.toml declares an empty dependency list. Either:
# via ComfyUI Manager: search "comfyui-friendly-string" (pack title), then install and restart
# or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/ZeronoFreya/ComfyUI-Friendly-String
then restart ComfyUI and hard-refresh the browser (Ctrl+Shift+R). That second step matters here: the pack ships a js/ directory that swaps the template widget for a real DOM widget, and a stale frontend will show you the raw load_text text field instead of the file picker.
One real requirement: the nodes are written against ComfyUI's newer backend authoring API (io.ComfyNode / io.Schema), which is why the pack contains no NODE_CLASS_MAPPINGS anywhere. That's "this pack is new," not "this pack is broken" - but a genuinely old ComfyUI will fail on the import.
Where people get burned
Plugging a Named Text node straight into CLIP Text Encode. You get the literal JSON - {"__fn_key__": "quality", "value": "masterpiece"} - fed to your encoder as prompt text. The JSON is the output by design; it only becomes readable text once Format Text consumes it.
Typo'd placeholders. {charater} yields nothing, and Clean Delimiters tidies the comma left behind, so the output looks perfectly well-formed. Just missing a chunk. If an image comes out oddly generic, check your names before you blame the checkpoint.
Editing a template file on disk after selecting it. The file is read once, in the browser, and the text is saved into the workflow. Rewriting the .txt doesn't change your graph - re-select it in the dropdown to reload. The upside is real, though: the template travels with the workflow, so sharing it doesn't depend on your folder layout.
Duplicate names. Two Named Text nodes with the same name won't error - the values get joined with your delimiter, in slot order. Occasionally on purpose, usually a copy-paste accident.
For a two-node pack with zero dependencies, this thing punches well above its weight: it's the missing string layer between your primitive text nodes and the encoder, and it costs you nothing to try.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| values | COMFY_AUTOGROW_V3 | — | |
| load_text | STRING | — | |
| f_string | STRING | — | |
| clean_delimiters | BOOLEAN | true | — |
| custom_delimiters | STRING | , | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| STRING | STRING | — |