/comfyui-node-outputs
ComfyUI node output types - NodeOutput, UI outputs, PreviewImage, PreviewMask, SavedImages, PreviewAudio, PreviewText, PreviewVideo. Use when returning results from nodes, displaying previews, or saving output files.
$ npx -y skills add jtydhr88/comfyui-custom-node-skills --skill comfyui-node-outputs --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/comfyui-node-outputs
Context preview
The summary Claude sees to decide when to auto-load this skill.
ComfyUI node output types - NodeOutput, UI outputs, PreviewImage, PreviewMask, SavedImages, PreviewAudio, PreviewText, PreviewVideo. Use when returning results from nodes, displaying previews, or saving output files.
SKILL.md
comfyui-node-outputs.SKILL.mdname: comfyui-node-outputs
description: ComfyUI node output types - NodeOutput, UI outputs, PreviewImage, PreviewMask, SavedImages, PreviewAudio, PreviewText, PreviewVideo. Use when returning results from nodes, displaying previews, or saving output files.
ComfyUI Node Outputs
Nodes return data through `io.NodeOutput`. V3 provides built-in UI helpers for previews and file saving.
Basic Output
class SimpleNode(io.ComfyNode):
@classmethod
def define_schema(cls):
return io.Schema(
node_id="SimpleNode",
display_name="Simple Node",
category="example",
inputs=[io.Float.Input("a"), io.Float.Input("b")],
outputs=[
io.Float.Output("SUM"),
io.Float.Output("PRODUCT"),
],
)
@classmethod
def execute(cls, a, b):
# Values must match output order
return io.NodeOutput(a + b, a * b)Output Configuration
io.Schema(
outputs=[
io.Image.Output("IMAGE"), # basic output
io.Int.Output("COUNT"), # integer output
io.Float.Output("VALUE", display_name="Result"), # custom display name
io.String.Output("TEXT", tooltip="The processed text"),
io.Image.Output("FRAMES", is_output_list=True), # outputs a list
],
)NodeOutput Variants
# Data only
return io.NodeOutput(image_tensor, mask_tensor)
# UI only (output node with no data outputs)
return io.NodeOutput(ui=ui.PreviewImage(images, cls=cls))
# Data + UI
return io.NodeOutput(image_tensor, ui=ui.PreviewImage(images, cls=cls))
# No output
return io.NodeOutput()
# Block execution
return io.NodeOutput(block_execution="Reason for blocking")
# Node expansion (positional args are outputs, not result= keyword)
return io.NodeOutput(output_ref, expand=graph.finalize())
UI Preview Helpers
Import `ui` from `comfy_api.latest`:
from comfy_api.latest import io, ui
PreviewImage
Display image previews on the node. Saves to temp directory automatically.
# Constructor: PreviewImage(image, animated=False, cls=None)
class PreviewNode(io.ComfyNode):
@classmethod
def define_schema(cls):
return io.Schema(
node_id="PreviewNode",
display_name="Preview Image",
category="image",
is_output_node=True,
inputs=[io.Image.Input("images")],
outputs=[],
hidden=[io.Hidden.prompt, io.Hidden.extra_pnginfo],
)
@classmethod
def execute(cls, images):
return io.NodeOutput(ui=ui.PreviewImage(images, cls=cls))PreviewMask
# Constructor: PreviewMask(mask, animated=False, cls=None)
# Auto-converts mask to 3-channel grayscale for display
return io.NodeOutput(ui=ui.PreviewMask(masks, cls=cls))
PreviewAudio
# Constructor: PreviewAudio(audio, cls=None)
# Saves as FLAC to temp directory
return io.NodeOutput(ui=ui.PreviewAudio(audio, cls=cls))
PreviewVideo
# Constructor: PreviewVideo(values: list[SavedResult | dict])
return io.NodeOutput(ui=ui.PreviewVideo(saved_video_results))
PreviewText
Display text output:
return io.NodeOutput(ui=ui.PreviewText(value))
PreviewUI3D
Display 3D model preview:
return io.NodeOutput(ui=ui.PreviewUI3D(
model_file=saved_result, # SavedResult for the 3D file
camera_info=camera_dict, # camera position/target/zoom
bg_image=image_tensor, # optional background image (via **kwargs)
))PreviewUI3DAdvanced
3D preview that also carries per-model transforms (used by the Preview3DAdvanced node):
return io.NodeOutput(ui=ui.PreviewUI3DAdvanced(
model_file=saved_result,
camera_info=camera_dict, # supports extended CameraInfo (quaternion/fov/near/far/frustum)
model_3d_info=transforms, # list[Model3DTransform]: position/quaternion/scale per model
))Saving Images
Using ImageSaveHelper
The `ui.ImageSaveHelper` class provides static methods for various image formats:
# Save as PNG (returns list[SavedResult])
results = ui.ImageSaveHelper.save_images(
images, # tensor [B,H,W,C]
filename_prefix="ComfyUI",
folder_type=io.FolderType.output, # output, temp, or input
cls=cls, # node class (for metadata)
compress_level=4,
)
# Save and get UI object directly (saves to output folder)
saved_ui = ui.ImageSaveHelper.get_save_images_ui(images, "ComfyUI", cls=cls)
return io.NodeOutput(ui=saved_ui)
# Save animated PNG
result = ui.ImageSaveHelper.save_animated_png(
images, "anim", io.FolderType.output, cls=cls, fps=12.0, compress_level=4
)
# Save animated PNG and get UI
saved_ui = ui.ImageSaveHelper.get_save_animated_png_ui(images, "anim", cls=cls, fps=12.0, compress_level=4)
# Save animated WebP
result = ui.ImageSaveHelper.save_animated_webp(
images, "anim", io.FolderType.output, cls=cls,
fps=12.0, lossless=False, quality=80, method=4
)
# Save animated WebP and get UI
saved_ui = ui.ImageSaveHelper.get_save_animated_webp_ui(
images, "anim", cls=cls, fps=12.0, lossless=False, quality=80, method=4
)**Simple save node example:**
class SaveImageNode(io.ComfyNode):
@classmethod
def define_schema(cls):
return io.Schema(
node_id="SaveImageNode",
display_name="Save Image",
category="image",
is_output_node=True,
inputs=[
io.Image.Input("images"),
io.String.Input("filename_prefix", default="ComfyUI"),
],
outputs=[],
hidden=[io.Hidden.prompt, io.Hidden.extra_pnginfo],
)
@classmethod
def execute(cls, images, filename_prefix):
saved = ui.ImageSaveHelper.get_save_images_ui(images, filename_prefix, cls=cRead more
name: comfyui-node-outputs description: ComfyUI node output types - NodeOutput, UI outputs, PreviewImage, PreviewMask, SavedImages, PreviewAudio, PreviewText, PreviewVideo. Use when returning results from nodes, displaying previews, or saving output files.
ComfyUI Node Outputs
Nodes return data through `io.NodeOutput`. V3 provides built-in UI helpers for previews and file saving.
Basic Output
class SimpleNode(io.ComfyNode):
@classmethod
def define_schema(cls):
return io.Schema(
node_id="SimpleNode",
display_name="Simple Node",
category="example",
inputs=[io.Float.Input("a"), io.Float.Input("b")],
outputs=[
io.Float.Output("SUM"),
io.Float.Output("PRODUCT"),
],
)
@classmethod
def execute(cls, a, b):
# Values must match output order
return io.NodeOutput(a + b, a * b)Output Configuration
io.Schema(
outputs=[
io.Image.Output("IMAGE"), # basic output
io.Int.Output("COUNT"), # integer output
io.Float.Output("VALUE", display_name="Result"), # custom display name
io.String.Output("TEXT", tooltip="The processed text"),
io.Image.Output("FRAMES", is_output_list=True), # outputs a list
],
)NodeOutput Variants
# Data only return io.NodeOutput(image_tensor, mask_tensor) # UI only (output node with no data outputs) return io.NodeOutput(ui=ui.PreviewImage(images, cls=cls)) # Data + UI return io.NodeOutput(image_tensor, ui=ui.PreviewImage(images, cls=cls)) # No output return io.NodeOutput() # Block execution return io.NodeOutput(block_execution="Reason for blocking") # Node expansion (positional args are outputs, not result= keyword) return io.NodeOutput(output_ref, expand=graph.finalize())
UI Preview Helpers
Import `ui` from `comfy_api.latest`:
from comfy_api.latest import io, ui
PreviewImage
Display image previews on the node. Saves to temp directory automatically.
# Constructor: PreviewImage(image, animated=False, cls=None)
class PreviewNode(io.ComfyNode):
@classmethod
def define_schema(cls):
return io.Schema(
node_id="PreviewNode",
display_name="Preview Image",
category="image",
is_output_node=True,
inputs=[io.Image.Input("images")],
outputs=[],
hidden=[io.Hidden.prompt, io.Hidden.extra_pnginfo],
)
@classmethod
def execute(cls, images):
return io.NodeOutput(ui=ui.PreviewImage(images, cls=cls))PreviewMask
# Constructor: PreviewMask(mask, animated=False, cls=None) # Auto-converts mask to 3-channel grayscale for display return io.NodeOutput(ui=ui.PreviewMask(masks, cls=cls))
PreviewAudio
# Constructor: PreviewAudio(audio, cls=None) # Saves as FLAC to temp directory return io.NodeOutput(ui=ui.PreviewAudio(audio, cls=cls))
PreviewVideo
# Constructor: PreviewVideo(values: list[SavedResult | dict]) return io.NodeOutput(ui=ui.PreviewVideo(saved_video_results))
PreviewText
Display text output:
return io.NodeOutput(ui=ui.PreviewText(value))
PreviewUI3D
Display 3D model preview:
return io.NodeOutput(ui=ui.PreviewUI3D(
model_file=saved_result, # SavedResult for the 3D file
camera_info=camera_dict, # camera position/target/zoom
bg_image=image_tensor, # optional background image (via **kwargs)
))PreviewUI3DAdvanced
3D preview that also carries per-model transforms (used by the Preview3DAdvanced node):
return io.NodeOutput(ui=ui.PreviewUI3DAdvanced(
model_file=saved_result,
camera_info=camera_dict, # supports extended CameraInfo (quaternion/fov/near/far/frustum)
model_3d_info=transforms, # list[Model3DTransform]: position/quaternion/scale per model
))Saving Images
Using ImageSaveHelper
The `ui.ImageSaveHelper` class provides static methods for various image formats:
# Save as PNG (returns list[SavedResult])
results = ui.ImageSaveHelper.save_images(
images, # tensor [B,H,W,C]
filename_prefix="ComfyUI",
folder_type=io.FolderType.output, # output, temp, or input
cls=cls, # node class (for metadata)
compress_level=4,
)
# Save and get UI object directly (saves to output folder)
saved_ui = ui.ImageSaveHelper.get_save_images_ui(images, "ComfyUI", cls=cls)
return io.NodeOutput(ui=saved_ui)
# Save animated PNG
result = ui.ImageSaveHelper.save_animated_png(
images, "anim", io.FolderType.output, cls=cls, fps=12.0, compress_level=4
)
# Save animated PNG and get UI
saved_ui = ui.ImageSaveHelper.get_save_animated_png_ui(images, "anim", cls=cls, fps=12.0, compress_level=4)
# Save animated WebP
result = ui.ImageSaveHelper.save_animated_webp(
images, "anim", io.FolderType.output, cls=cls,
fps=12.0, lossless=False, quality=80, method=4
)
# Save animated WebP and get UI
saved_ui = ui.ImageSaveHelper.get_save_animated_webp_ui(
images, "anim", cls=cls, fps=12.0, lossless=False, quality=80, method=4
)**Simple save node example:**
class SaveImageNode(io.ComfyNode):
@classmethod
def define_schema(cls):
return io.Schema(
node_id="SaveImageNode",
display_name="Save Image",
category="image",
is_output_node=True,
inputs=[
io.Image.Input("images"),
io.String.Input("filename_prefix", default="ComfyUI"),
],
outputs=[],
hidden=[io.Hidden.prompt, io.Hidden.extra_pnginfo],
)
@classmethod
def execute(cls, images, filename_prefix):
saved = ui.ImageSaveHelper.get_save_images_ui(images, filename_prefix, cls=cA curated collection of agent skills (for Claude Code and OpenAI Codex) for developing ComfyUI custom nodes. These skills give the agent comprehensive knowledge of the ComfyUI node system, covering both the V3 (recommended) and V1 (legacy) APIs.
Repo: jtydhr88/comfyui-custom-node-skills
Other skills on comfyui-custom-node-skills.
- /comfyui-node-advanced
ComfyUI advanced node patterns - MatchType, Autogrow, DynamicCombo, node expansion, MultiType, wildcard inputs. Use when building complex nodes with dynamic inputs, type matching, or node expansion.
Open skill - /comfyui-node-basics
ComfyUI custom node fundamentals - V3 node structure, Schema, inputs/outputs, registration. Use when creating new ComfyUI custom nodes, defining node classes, or setting up a custom node project.
Open skill - /comfyui-node-datatypes
ComfyUI data types - IMAGE, LATENT, MASK, CONDITIONING, MODEL, CLIP, VAE, AUDIO, VIDEO, 3D types, widget types, and custom types. Use when working with ComfyUI tensors, model types, or defining input/output data types.
Open skill - /comfyui-node-frontend
ComfyUI frontend JavaScript extensions - hooks, widgets, sidebar tabs, commands, settings, toasts, dialogs. Use when adding UI features to custom nodes, creating custom widgets, or extending the ComfyUI frontend.
Open skill - /comfyui-node-inputs
ComfyUI node input types - INT, FLOAT, STRING, BOOLEAN, COMBO widgets, hidden inputs, optional inputs, lazy inputs, force_input. Use when configuring node inputs, adding widgets, or customizing input behavior.
Open skill - /comfyui-node-lifecycle
ComfyUI node execution lifecycle - caching, fingerprint_inputs/IS_CHANGED, validate_inputs/VALIDATE_INPUTS, check_lazy_status, execution order. Use when debugging execution, implementing caching control, input validation, or understanding execution flow.
Open skill

