Documentation Index Fetch the complete documentation index at: https://mintlify.com/Comfy-Org/ComfyUI/llms.txt
Use this file to discover all available pages before exploring further.
Custom nodes extend ComfyUI’s functionality by adding new processing capabilities to your workflows. This guide covers creating nodes using the modern ComfyUI API.
Basic Node Structure
Custom nodes in ComfyUI are Python classes that inherit from io.ComfyNode and define a schema for inputs, outputs, and execution logic.
Minimal Example
from comfy_api.latest import ComfyExtension, io
class MyCustomNode ( io . ComfyNode ):
@ classmethod
def define_schema ( cls ) -> io.Schema:
return io.Schema(
node_id = "MyCustomNode" ,
display_name = "My Custom Node" ,
category = "custom" ,
inputs = [
io.Image.Input( "image" ),
],
outputs = [
io.Image.Output(),
],
)
@ classmethod
def execute ( cls , image ) -> io.NodeOutput:
# Process the image
processed_image = image # Your processing here
return io.NodeOutput(processed_image)
Schema Definition
The define_schema() method tells ComfyUI about your node’s metadata, inputs, and outputs.
Schema Parameters
Unique identifier for your node
Human-readable name shown in the UI
Category path for organizing nodes (e.g., “image/processing”)
List of input definitions
List of output definitions
ComfyUI provides various input types for different data:
io.Image.Input(
"image" ,
optional = False ,
tooltip = "The input image to process"
)
io.Int.Input(
"int_field" ,
min = 0 ,
max = 4096 ,
step = 64 ,
default = 512 ,
display_mode = io.NumberDisplay.slider,
tooltip = "Integer value"
)
io.String.Input(
"text_field" ,
multiline = True ,
default = "Hello world!" ,
tooltip = "Text input"
)
io.Combo.Input(
"mode" ,
options = [ "option1" , "option2" , "option3" ],
default = "option1" ,
tooltip = "Select an option"
)
Complete Example
Here’s a complete example from the ComfyUI source code:
from typing_extensions import override
from comfy_api.latest import ComfyExtension, io
class Example ( io . ComfyNode ):
"""
An example node that processes images
"""
@ classmethod
def define_schema ( cls ) -> io.Schema:
return io.Schema(
node_id = "Example" ,
display_name = "Example Node" ,
category = "Example" ,
inputs = [
io.Image.Input( "image" ),
io.Int.Input(
"int_field" ,
min = 0 ,
max = 4096 ,
step = 64 ,
display_mode = io.NumberDisplay.number,
lazy = True ,
),
io.Float.Input(
"float_field" ,
default = 1.0 ,
min = 0.0 ,
max = 10.0 ,
step = 0.01 ,
round = 0.001 ,
display_mode = io.NumberDisplay.number,
lazy = True ,
),
io.Combo.Input(
"print_to_screen" ,
options = [ "enable" , "disable" ]
),
io.String.Input(
"string_field" ,
multiline = False ,
default = "Hello world!" ,
lazy = True ,
)
],
outputs = [
io.Image.Output(),
],
)
@ classmethod
def execute ( cls , image , string_field , int_field ,
float_field , print_to_screen ) -> io.NodeOutput:
if print_to_screen == "enable" :
print ( f """Your input contains:
string_field: { string_field }
int_field: { int_field }
float_field: { float_field }
""" )
# Process image (invert in this example)
image = 1.0 - image
return io.NodeOutput(image)
class ExampleExtension ( ComfyExtension ):
@override
async def get_node_list ( self ) -> list[type[io.ComfyNode]]:
return [Example]
async def comfy_entrypoint () -> ExampleExtension:
return ExampleExtension()
The comfy_entrypoint() function is the entry point that ComfyUI calls to load your extension and its nodes.
Lazy Evaluation
Inputs marked with lazy=True are only evaluated when needed, which can improve performance:
@ classmethod
def check_lazy_status ( cls , image , string_field , int_field ,
float_field , print_to_screen ):
"""
Return a list of input names that need to be evaluated.
Unevaluated inputs will have the value None.
"""
if print_to_screen == "enable" :
return [ "int_field" , "float_field" , "string_field" ]
else :
return []
Use check_lazy_status() to control which lazy inputs are evaluated based on other input values.
Control when your node re-executes using fingerprint_inputs():
@ classmethod
def fingerprint_inputs ( cls , image , string_field , int_field ,
float_field , print_to_screen ):
"""
Return a value that changes when the node should re-execute.
If this returns the same value as the last execution, the node
will not run again.
"""
import hashlib
# Example: hash the image data
return hashlib.md5(image.numpy().tobytes()).hexdigest()
Extension Class
Wrap your nodes in a ComfyExtension class:
class MyExtension ( ComfyExtension ):
async def on_load ( self ) -> None :
"""Called when the extension loads"""
print ( "My extension loaded!" )
@override
async def get_node_list ( self ) -> list[type[io.ComfyNode]]:
"""Return all nodes provided by this extension"""
return [
MyCustomNode,
AnotherNode,
]
async def comfy_entrypoint () -> MyExtension:
return MyExtension()
Next Steps
Node API Reference Explore the complete API reference
Best Practices Learn best practices for node development