Portal Community

Complete Minimal Example

// MyCustomNode.tsx
import { BaseNode, BaseNodeProps } from 'flow-studio-designer/Base/BaseNode';
import { CustomHandle } from 'flow-studio-designer/Handles/CustomHandle';
import { withNodeHooks } from 'flow-studio-designer/Base/withNodeHooks';
import { Position } from 'reactflow';

class MyCustomNodeClass extends BaseNode {
  renderBody() {
    const { data } = this.props;

    return (
      <>
        {/* Target handle — data arrives here */}
        <CustomHandle
          type="target"
          position={Position.Left}
          id="main"
          isRequired
        />

        {/* Node visual */}
        <div className="node-header" style={{ background: data.color }}>
          <i className={data.icon} />
          <span className="node-title">{data.label}</span>
        </div>
        <div className="node-body">
          <span className="node-type-label">My Custom Node</span>
        </div>

        {/* Source handle — data flows out here */}
        <CustomHandle
          type="source"
          position={Position.Right}
          id="main"
          isMain
        />
      </>
    );
  }
}

// IMPORTANT: always export wrapped with withNodeHooks
export const MyCustomNode = withNodeHooks(MyCustomNodeClass);

Required CSS Classes

The node-wrapper class is applied by BaseNode.render(). Your renderBody() should use:

ClassPurpose
node-headerColoured top bar with icon and title
node-bodyContent area below the header
node-titleTitle text in the header
node-type-labelSmall secondary label

These classes are defined in the shared Flow Studio CSS included in the designer package.