examples/node-api. Each pack demonstrates one family of published JavaScript
APIs without importing ComfyUI source files, patching generated node classes, or
using the legacy app global.
These are executable examples, not isolated fragments. The frontend repository
also contains a saved workflow and browser tests that exercise registration,
prompt resolution, widget events, backend calls, graph batching, undo, and a
real backend run.
The example packs keep their Python deliberately small because they teach the
published frontend API. Each pack’s
v2/__init__.py only registers the
extension and exposes WEB_DIRECTORY; the behavior under test is the
JavaScript module.Find and install the examples
In a checkout of the ComfyUI frontend repository, the files are here:custom_nodes directory, then
restart ComfyUI:
API Examples. Each pack README gives a short
exercise for the nodes it installs.
These checkout examples are arranged for direct local testing. When publishing
a converted V2 pack, retain the complete V1 distribution at the pack root and
put the complete V2 replacement distribution under v2/. Do not flatten these
example directories into a converted pack or treat v2/ as an overlay.
Start with a frontend-only node
how_to_frontend_nodes is the smallest starting point. Its Constant Text node
stays in the saved workflow but resolves to a literal before the backend prompt
is sent:
forMajor(2)pins the contract the module expects;require()fails early with the missing capability name;resolve()describes the output without mutating the graph or a prompt draft.
forwardTo, a same-group text supplier, and
a First Connected node that adds a new input whenever its final input becomes
connected:
Choose the right widget ownership model
how_to_widgets places four widget approaches next to each other:
- ordinary declared widgets with additive event listeners;
- a host-rendered canvas widget;
- a mounted, pack-owned DOM control;
- a custom renderer for a Python-declared input type.
activate and update another widget through
its handle:
defs.defineWidgetType(). The example returns a cleanup function that removes
DOM listeners and API subscriptions when the mounted widget is destroyed:
event.context === 'prompt'.
Add graph behavior without internal objects
how_to_graph_interaction demonstrates behavior that legacy extensions often
implemented through LiteGraph instances or canvas hooks.
The Lifecycle Badge node stores pack-owned state in a Map, returns that state
from onSerialize, restores it in onConfigured, and releases the badge in
onRemoved. This makes ownership and cleanup visible in the code.
The Graph Builder node uses a synchronous batch so adding, connecting, and
selecting two nodes becomes one undoable edit:
Connect execution to application services
how_to_execution combines small backend nodes and routes with supported
frontend services. Its Text Output node adds a context-menu action that queues
only that node and updates a badge from the correlated result:
commands.has()andcommands.run()for the host mask editor;backend.fetch()for a pack-owned route;backend.on()for a validated custom backend event;settings.declare()for a preference;commands.register()for a command and keybinding;storage.set()andstorage.get()for named per-user content.
Use the examples as a pattern library
Do not copy an entire pack when one focused pattern is enough. Start with the example closest to the behavior you need, copy its capability requirements and lifecycle structure, then rename its types, settings, commands, storage keys, and backend events into a namespace owned by your pack. Before release:- verify each retained
require()names a capability the feature truly needs; - remove demo categories and identifiers;
- keep cleanup paired with every retained subscription or mounted control;
- test save, reload, duplicate, delete, undo, and execution behavior;
- test the complete V2 replacement under the pack’s
v2/directory.