Nodes/Prompt Preset Builder/Prompt Preset Builder
ComfyUI Node

Prompt Preset Builder

Build it from named parts and save the whole thing

By aiai-r·Created about a month ago·Updated about a month ago· 0
Prompt Preset Builder
    • prompt
    preset_data[]
    separator,
    seed0

    If you've ever had a positive-prompt box that's four lines long and you keep trimming it down, pasting it back, and praying you didn't break the one tag that was holding the whole composition together, this node is aimed at you. Prompt Preset Builder is a text-plumbing node that splits your prompt into named, individually togglable elements - say character, hair, outfit, background, quality tags - and saves the whole set as a named preset you can flip between. It's not a sampler, it's not an upscaler; it's the thing that makes your prompt a reusable asset instead of a string you hand-edit every time.

    The output is a single STRING, so it drops straight into a CLIP Text Encode positive box - on SDXL-lineage models where commas and booru tags still rule, and on LLM-encoded models where a structured, block-organized prompt is the new high-leverage move. There's nothing model-specific here, so it won't go stale when the checkpoint of the month rotates.

    How it works

    Mechanically it's a glorified text joiner with an opinionated front end. Each element is one row: a free-text label plus a prompt box, with a checkbox to include or exclude it and / to reorder. The enabled elements get joined with separator (default ", "), and anything that resolves to empty is skipped so you never get a dangling comma. The rows live as JSON in a hidden preset_data STRING widget, which is what actually gets embedded in your workflow file.

    The front end does the clever stuff. Only the row you click expands (a hand-set height is remembered), and past eight elements the list scrolls inside the node instead of growing forever. The prompt boxes are built through ComfyUI's own ComfyWidgets.STRING factory, so your autocomplete extension - ComfyUI-Autocomplete-Plus and friends - treats them exactly like the box on a normal CLIP Text Encode.

    The inputs and outputs that matter

    There are only three inputs and all but one are background:

    • separator - what joins the enabled elements. Default ", " is right for tag-style prompting; swap in a \n (the node expands it for you) for models that want block structure.
    • seed - an INT with control_after_generate, same as a sampler seed. It only randomizes the {...} picks in your elements, not the denoise - and control_after_generate: fixed is how you lock a result.
    • preset_data - the hidden JSON blob. You don't touch this; the UI manages it.

    The output, prompt (STRING), is the joined result, and after a run the string that was actually produced is shown at the bottom of the node. That last bit matters more than it sounds: the classic dynamic-prompt complaint has always been "great image, but what prompt actually made it?" - here the answer is right there on the node.

    The random syntax

    Inside any element, {a|b|c} picks one option at random, and this pack's flavor is stricter than the built-in: ComfyUI's native dynamicPrompts toggle only handles plain {a|b|c}, so the node resolves the syntax itself in Python to support weights and nesting:

    {|wavy hair|0.4::{|high|low|side} ponytail|0.3::{low|short} hair}
    

    0.4:: weights an option, empty options are legal (that | right after { gives "emit nothing" a real chance), groups nest, and \{ \} \| escape literals. A missing } just closes at end of text.

    Installing it

    Zero dependencies beyond what ComfyUI already ships (aiohttp comes with the server) and no model downloads - this is pure text.

    • ComfyUI Manager: search "Prompt Preset Builder", install, restart.
    • Manual: cd ComfyUI/custom_nodes && git clone https://github.com/aiai-r/ComfyUI-PromptPresetBuilder, then restart ComfyUI.

    Common gotchas

    Your presets live in a file, not in your workflow. They're stored at user/<user>/prompt_preset_builder/presets.json, grouped by label (portrait/basic and landscape/basic are two separate presets that happen to share a name). Only the currently-loaded elements ride along when you share a workflow JSON - your whole preset library does not. First run on a new machine means rebuilding or copying that file.

    Save overwrites the selected preset silently. It only asks for a destination when nothing is selected, so if you're experimenting, use Save as until you're sure. A * next to the preset name means your elements have drifted from what's saved; only then do switching presets and New warn you before discarding.

    Watch the seed trap. Like every control_after_generate widget, the shown seed is the one that runs next - the resolved string at the bottom of the node is your ground truth, so read it from there instead.

    One light aside: this is the rare custom node whose README is honestly better written than most - read it once if you want every UI gesture catalogued. Otherwise, add an element, type a fragment, save a preset, and stop retyping the same three lines forever.

    Categoryutils/prompt

    Inputs (3)

    NameTypeDefaultDescription
    preset_dataSTRING[]
    separatorSTRING,
    seedINT00–18446744073709550000

    Outputs (1)

    NameTypeDescription
    promptSTRING