custom-node-definition
Development pattern for creating and rendering custom nodes in react-wireflow.
Install
mkdir -p .claude/skills/custom-node-definition && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/17170" && unzip -o skill.zip -d .claude/skills/custom-node-definition && rm skill.zipInstalls to .claude/skills/custom-node-definition
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Create custom node definitions with renderers, ports, external data, and constraints. Use when implementing new node types, custom node appearances, or node-specific behaviors.Key capabilities
- →Define basic node structure with type, display name, and category
- →Create custom node renderers for appearance
- →Configure input and output ports with data types and limits
- →Implement custom validation for port connections
- →Integrate external data loading and updating for nodes
- →Define node-level connection validation and constraints
How it works
The skill guides the creation of custom node definitions in `react-wireflow` by providing structures for basic node properties, renderers, port configurations, external data integration, and validation rules.
Inputs & outputs
When to use custom-node-definition
- →Create custom node types
- →Implement node renderers
- →Define node-specific ports
About this skill
Custom Node Definition
This skill covers creating custom node definitions in react-wireflow.
Basic NodeDefinition Structure
import type { NodeDefinition } from "react-wireflow";
const MyNodeDefinition: NodeDefinition = {
type: "my-node", // Unique identifier
displayName: "My Node", // Display name in UI
description: "Description for palette",
category: "My Category", // For grouping in palette
// Default values for new nodes
defaultData: { title: "New Node" },
defaultSize: { width: 200, height: 100 },
defaultResizable: true,
// Port definitions
ports: [
{ id: "input", type: "input", position: "left", label: "Input" },
{ id: "output", type: "output", position: "right", label: "Output" },
],
};
Custom Node Renderer
Use renderNode for custom node appearance:
import type { NodeDefinition, NodeRendererProps } from "react-wireflow";
import { NodeResizer } from "react-wireflow";
type TaskData = {
title: string;
status: "todo" | "done";
};
const TaskNodeRenderer = ({
node,
isSelected,
isDragging,
isResizing,
isEditing,
onStartEdit,
onUpdateNode,
}: NodeRendererProps<TaskData>) => {
const data = node.data;
return (
<NodeResizer node={node} defaultWidth={200} defaultHeight={100}>
{() => (
<div
className="task-node"
data-selected={isSelected}
data-status={data.status}
onDoubleClick={onStartEdit}
>
<h3>{data.title}</h3>
<span>{data.status}</span>
</div>
)}
</NodeResizer>
);
};
const TaskNodeDefinition: NodeDefinition<TaskData> = {
type: "task",
displayName: "Task",
defaultData: { title: "New Task", status: "todo" },
ports: [...],
renderNode: TaskNodeRenderer,
};
NodeRendererProps Reference
type NodeRendererProps<TData> = {
node: Node & { data: TData };
isSelected: boolean;
isDragging: boolean;
isResizing: boolean;
isEditing: boolean;
externalData: unknown;
isLoadingExternalData: boolean;
externalDataError: Error | null;
onStartEdit: () => void;
onUpdateNode: (updates: Partial<Node>) => void;
};
Port Configuration
Basic Ports
ports: [
{ id: "in", type: "input", position: "left", label: "Input" },
{ id: "out", type: "output", position: "right", label: "Output" },
]
Port with Data Type
{
id: "number-out",
type: "output",
position: "right",
label: "Number",
dataType: "number", // Single type
// or
dataTypes: ["number", "int"], // Multiple compatible types
}
Port with Connection Limits
{
id: "multi-input",
type: "input",
position: "left",
label: "Multi",
maxConnections: 5, // or "unlimited"
}
Port with Custom Validation
{
id: "exclusive",
type: "output",
position: "right",
label: "Exclusive",
canConnect: (context) => {
// Only connect to specific node types
return context.toNode?.type === "receiver";
},
}
Dynamic Ports
{
id: "item",
type: "input",
position: "left",
label: "Item",
instances: (context) => context.node.data.itemCount ?? 1,
createPortId: (ctx) => `item-${ctx.index}`,
createPortLabel: (ctx) => `Item ${ctx.index + 1}`,
}
Absolute Port Position
{
id: "center",
type: "output",
label: "Center",
position: {
mode: "absolute",
x: 0.5, // 50% from left
y: 0.5, // 50% from top
unit: "fraction",
},
}
External Data Integration
For nodes that load data from external sources:
import type { ExternalDataReference } from "react-wireflow";
const TaskNodeDefinition: NodeDefinition = {
type: "task",
displayName: "Task",
ports: [...],
loadExternalData: async (ref: ExternalDataReference) => {
const response = await fetch(`/api/tasks/${ref.id}`);
return response.json();
},
updateExternalData: async (ref: ExternalDataReference, data: unknown) => {
await fetch(`/api/tasks/${ref.id}`, {
method: "PUT",
body: JSON.stringify(data),
});
},
renderNode: TaskNodeRenderer,
renderInspector: TaskInspectorRenderer,
};
// Provide external data refs to NodeEditor:
<NodeEditor
initialData={data}
nodeDefinitions={[TaskNodeDefinition]}
externalDataRefs={{
"node-1": { id: "task-123", type: "task" },
"node-2": { id: "task-456", type: "task" },
}}
/>
Node-Level Connection Validation
const HubNodeDefinition: NodeDefinition = {
type: "hub",
displayName: "Hub",
ports: [...],
validateConnection: (fromPort, toPort) => {
// Custom validation logic
// fromPort is always OUTPUT, toPort is always INPUT
console.log(`Validating: ${fromPort.nodeId} -> ${toPort.nodeId}`);
return true; // or false to reject
},
};
Node Constraints
import type { NodeConstraint, ConstraintContext } from "react-wireflow";
const maxNodesConstraint: NodeConstraint = {
id: "max-nodes",
name: "Maximum Nodes",
description: "Limit total nodes of this type",
blocking: true,
appliesTo: ["create"],
validate: (context: ConstraintContext) => {
const count = Object.values(context.allNodes)
.filter(n => n.type === context.node.type).length;
if (count >= 5) {
return {
isValid: false,
violations: [{
type: "max-nodes",
message: "Maximum 5 nodes allowed",
severity: "error",
}],
};
}
return { isValid: true, violations: [] };
},
};
const LimitedNodeDefinition: NodeDefinition = {
type: "limited",
displayName: "Limited Node",
constraints: [maxNodesConstraint],
maxPerFlow: 5, // Built-in limit option
};
Custom Port Positions
Override default port positioning:
const CustomLayoutNode: NodeDefinition = {
type: "custom-layout",
displayName: "Custom Layout",
ports: [...],
computePortPositions: (context) => {
const { node, ports, nodeSize, defaultCompute } = context;
// Use default for most ports
const positions = defaultCompute(ports);
// Override specific port
const specialPort = ports.find(p => p.id === "special");
if (specialPort) {
positions.set("special", {
renderPosition: { x: nodeSize.width / 2, y: 0 },
connectionPoint: {
x: node.position.x + nodeSize.width / 2,
y: node.position.y,
},
});
}
return positions;
},
};
Node Behaviors
const GroupNodeDefinition: NodeDefinition = {
type: "group",
displayName: "Group",
behaviors: ["group"], // Enables group container behavior
};
const AppearanceNodeDefinition: NodeDefinition = {
type: "styled",
displayName: "Styled Node",
behaviors: ["appearance", "node"], // Custom appearance + standard node
disableOutline: true, // Disable default selection styling
};
Visual State
const ErrorNodeDefinition: NodeDefinition = {
type: "error",
displayName: "Error Node",
visualState: "error", // "info" | "success" | "warning" | "error" | "disabled"
};
Categories
// Single category
category: "Math"
// Multiple categories
category: ["Math", "Utilities"]
// Hierarchical category
category: "custom/ui/buttons"
Type-Safe Helper
import { createNodeDefinition, asNodeDefinition } from "react-wireflow";
// Type-safe creation
const TypedNode = createNodeDefinition<MyDataType>({
type: "typed",
displayName: "Typed Node",
defaultData: { value: 0 },
ports: [],
});
// Convert to base type for arrays
const definitions: NodeDefinition[] = [
asNodeDefinition(TypedNode),
];
Example Files
- Basic custom node:
src/examples/demos/custom/nodes/custom-node/CustomNodeExample.tsx - Advanced nodes:
src/examples/demos/advanced/advanced-node/ - Connection rules:
src/examples/demos/custom/connections/connection-rules/ - Constrained nodes:
src/examples/demos/basic/constrained-nodes/
When not to use it
- →When creating a pure decision-tree skill that does not require tools
- →When the goal is to define node behaviors not related to rendering or data
- →When the goal is to define visual states not related to rendering or data
Limitations
- →The skill is specific to `react-wireflow` node definitions.
- →The skill focuses on defining node structure, rendering, and data, not general application logic.
- →The skill requires understanding of TypeScript for type-safe definitions.
How it compares
This workflow provides structured patterns and type-safe helpers for defining custom nodes in `react-wireflow`, enabling specific appearances and behaviors, unlike generic node implementations.
Compared to similar skills
custom-node-definition side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| custom-node-definition (this skill) | 0 | 8mo | No flags | Advanced |
| web-artifacts-builder | 49 | 4mo | Review | Intermediate |
| radix-ui-design-system | 33 | 2mo | Review | Intermediate |
| figma-integration | 23 | 7mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
web-artifacts-builder
anthropics
Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.
radix-ui-design-system
sickn33
Build accessible design systems with Radix UI primitives. Headless component customization, theming strategies, and compound component patterns for production-grade UI libraries.
figma-integration
duongdev
Guides design-to-code workflow using Figma integration. Helps extract designs, analyze components, and generate implementation specs. Auto-activates when users mention Figma URLs, design implementation, component conversion, or design-to-code workflows. Works with /ccpm:planning:design-ui, design-approve, design-refine, and /ccpm:utils:figma-refresh commands.
figma-implement-design
openai
Translate Figma nodes into production-ready code with 1:1 visual fidelity using the Figma MCP workflow (design context, screenshots, assets, and project-convention translation). Trigger when the user provides Figma URLs or node IDs, or asks to implement designs or components that must match Figma specs. Requires a working Figma MCP server connection.
react-flow-node-ts
microsoft
Create React Flow node components with TypeScript types, handles, and Zustand integration. Use when building custom nodes for React Flow canvas, creating visual workflow editors, or implementing node-based UI components.
frontend-ui-dark-ts
microsoft
Build dark-themed React applications using Tailwind CSS with custom theming, glassmorphism effects, and Framer Motion animations. Use when creating dashboards, admin panels, or data-rich interfaces with a refined dark aesthetic.