Template String
The tiny {} node that keeps your ComfyUI prompts from turning into spaghetti
- text
The whole point, in one line
Template String is a string-interpolation node: you write a prompt with {} placeholders, wire up to ten string values into it, and it stitches them together in order. That's it. No API, no key, no model downloads, no dependencies - a single render() that runs a regex and returns one string. It exists to solve a very ComfyUI problem: prompts that are assembled from other nodes instead of typed by hand.
Once your workflow grows past "type a prompt, hit queue" - once you're pulling subjects from a Set/Get node, model names from a LoRA loader, or tags from a batch tool - you stop typing prompts and start building them. That's where a node like this earns its place. It's the text-side cousin of what rgthree's Power Puter does for numbers: glue together values that live elsewhere in the graph.
How the substitution actually works
The mechanism is worth understanding because it has one quirk that bites people. The template can contain any number of {...} placeholders, and whatever you put inside the braces - {subject}, {1}, {color_of_the_hair} - is treated as a comment. Only position matters, never the label.
The node goes through your connected inputs input_1 through input_10, collects the ones that are actually wired, then replaces placeholders left to right with those values, one at a time:
template: a {subject} in a {setting}, {lighting} lighting, {quality}
input_1: burger
input_2: dim restaurant
input_3: cinematic
input_4: 8k
→ a burger in a dim restaurant, cinematic lighting, 8k
Notice {subject} was never matched against the name subject - the value burger just happened to be first in line. This is the trap: the labels are decoration for humans reading the workflow, not variables. If you unplug input_1 and leave input_2 wired, that value slides into the first placeholder, because substitution is purely positional. It's also why reordering your template doesn't reorder your inputs - inputs are consumed in slot order, full stop.
Unused placeholders (more braces than connected inputs) are left in the string literally, and extra inputs are silently ignored. There's no validation, which is the other edge of the simplicity.
The inputs and outputs that matter
Only two things matter here, so this is a short list:
- template (required, multiline) - your prompt with
{}placeholders. Default isa {}, {}, {}. - input_1 … input_10 (optional) - the values to substitute. All ten are optional; leave unwired ones alone. They're
forceInput, so in current ComfyUI you can either type a static value into them or drag a wire from any STRING output. - text (output) - the finished string. Wire it into a CLIPTextEncode node's text input, into a Save Image filename field, or back into another text node.
Install
It's a two-minute install with nothing heavy behind it:
cd ComfyUI/custom_nodes
git clone https://github.com/tennantje/comfyui-template-string
Then restart ComfyUI. Easier still: open ComfyUI Manager, search for "comfyui-template-string" (or "Template String"), and click Install. There are zero pip dependencies - the project's pyproject.toml lists none - and no model files to fetch. If install feels too easy, that's because it is.
Where people get burned
The classic failure is expecting the braces to behave like named slots. They don't. {subject} isn't bound to a "subject" input; it's bound to whatever is wired to the next available slot. Label clearly, but remember the labels are comments.
Second gotcha: a template with more placeholders than wired inputs passes literal {} into your prompt, and CLIPTextEncode will happily tokenize that garbage. Count your braces before you queue a batch.
And one honest caveat: this is not a dynamic-prompt or wildcard node. It does zero randomization - no __wildcard__ expansion, no lists, no dice rolls. If you want random prompt variations, that's a different tool entirely. Template String's job is narrower and it's great at it: assembling a readable string from values the rest of your graph already computed.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| template | STRING | a {}, {}, {} | — |
| input_1opt | STRING | — | |
| input_2opt | STRING | — | |
| input_3opt | STRING | — | |
| input_4opt | STRING | — | |
| input_5opt | STRING | — | |
| input_6opt | STRING | — | |
| input_7opt | STRING | — | |
| input_8opt | STRING | — | |
| input_9opt | STRING | — | |
| input_10opt | STRING | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| text | STRING | — |