Prompt Groups FunCode
Build prompts from named, switchable building blocks
- prompt
If you've ever stared at a 400-word prompt and wished you could turn it on and off by section, Prompt Groups FunCode is aimed at you. It's a structured prompt composer: you define named groups - "character", "clothing", "background", "quality tags", whatever - and each group has its own text box, its own enable switch, and optionally its own external input. The node joins the enabled groups into one prompt string.
The shape of the problem is the standard ComfyUI one: a positive prompt is really several concerns glued together, and gluing them by hand means editing one giant string whenever you swap a character or drop a background. This node splits the concern into blocks, which is the same "fight repetition and illegibility" instinct behind the whole plumbing layer - one authoritative source per block, then combine.
How it works
Configuration lives in groups_json, a JSON string where each group looks like:
{"id": "group_1", "name": "Character", "input_slot": 1, "text": "1girl, silver hair", "enabled": true}
At run time the node walks the enabled groups in order, takes each one's text (or the wired external text for that group, if input_slot maps to one), normalizes it - strips empty lines, and appends a trailing comma if the group doesn't already end in one - then joins groups with blank lines. The trailing-comma normalization is the quiet genius: each block ends cleanly, so the concatenated prompt doesn't merge words together.
There's also a front end. The node renders its own editor with a textarea per group, add/remove buttons, an input-slot picker, and a preset system that saves/loads named prompt sets to ComfyUI_FunCode/prompt_groups/presets.json (with folder support), so a character block you reuse across workflows is a couple of clicks away.
Inputs and output
- groups_json (multiline) - the JSON defining your groups. The default payload is one empty "Group 1"; edit it, or drive it from another node that emits JSON.
- group_1_text … group_50_text - optional string inputs, one per possible slot. Wire a text node into a group's slot and it overrides that group's baked-in
text. This is how you keep a group's structure fixed while swapping its content from elsewhere in the graph. - Output: prompt (STRING) - the joined result, ready for a CLIP Text Encode or wherever your prompt text goes.
Installing
cd ComfyUI/custom_nodes
git clone https://github.com/kaiery/ComfyUI_FunCode
Restart ComfyUI and refresh the browser tab - the group editor is frontend JS, so a stale page won't show it. Category is FunCode/Prompt. No model downloads, no pip deps.
Common issues
- Prompt reads as one run-on sentence - each group is meant to end with a comma (the node adds one if missing), but if your external inputs end in stray whitespace or mid-sentence text, the join can't fix that for you. Keep blocks comma-terminated.
- A group's text isn't showing - check
enabled. Disabled groups are skipped entirely, which is also the feature: that's your "turn the background off for this batch" switch. - Wired input ignored -
input_slotmaps to whichgroup_N_textsocket the group reads. If the slot doesn't match the socket you wired, the group falls back to its own text. Slot 1 → group_1_text, slot 7 → group_7_text, and so on. - Presets vanish after reinstall - presets live in the pack's
prompt_groups/presets.jsonon disk, which a fresh clone overwrites. Back it up if you build a library.
The honest take: if you already have a prompt-assembler you love, this won't convert you. But if your prompts are a wall of text you edit with find-and-replace, turning them into switchable named blocks is the kind of change you feel on every single generation afterward.
Inputs (51)
| Name | Type | Default | Description |
|---|---|---|---|
| groups_json | STRING | {"version": 1, "groups": [{"id": "group_1", "name": "Group 1", "input_slot": 1, "text": "", "enabled": true}]} | — |
| group_1_textopt | STRING | — | |
| group_2_textopt | STRING | — | |
| group_3_textopt | STRING | — | |
| group_4_textopt | STRING | — | |
| group_5_textopt | STRING | — | |
| group_6_textopt | STRING | — | |
| group_7_textopt | STRING | — | |
| group_8_textopt | STRING | — | |
| group_9_textopt | STRING | — | |
| group_10_textopt | STRING | — | |
| group_11_textopt | STRING | — | |
| group_12_textopt | STRING | — | |
| group_13_textopt | STRING | — | |
| group_14_textopt | STRING | — | |
| group_15_textopt | STRING | — | |
| group_16_textopt | STRING | — | |
| group_17_textopt | STRING | — | |
| group_18_textopt | STRING | — | |
| group_19_textopt | STRING | — | |
| group_20_textopt | STRING | — | |
| group_21_textopt | STRING | — | |
| group_22_textopt | STRING | — | |
| group_23_textopt | STRING | — | |
| group_24_textopt | STRING | — | |
| group_25_textopt | STRING | — | |
| group_26_textopt | STRING | — | |
| group_27_textopt | STRING | — | |
| group_28_textopt | STRING | — | |
| group_29_textopt | STRING | — | |
| group_30_textopt | STRING | — | |
| group_31_textopt | STRING | — | |
| group_32_textopt | STRING | — | |
| group_33_textopt | STRING | — | |
| group_34_textopt | STRING | — | |
| group_35_textopt | STRING | — | |
| group_36_textopt | STRING | — | |
| group_37_textopt | STRING | — | |
| group_38_textopt | STRING | — | |
| group_39_textopt | STRING | — | |
| group_40_textopt | STRING | — | |
| group_41_textopt | STRING | — | |
| group_42_textopt | STRING | — | |
| group_43_textopt | STRING | — | |
| group_44_textopt | STRING | — | |
| group_45_textopt | STRING | — | |
| group_46_textopt | STRING | — | |
| group_47_textopt | STRING | — | |
| group_48_textopt | STRING | — | |
| group_49_textopt | STRING | — | |
| group_50_textopt | STRING | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| prompt | STRING | — |