Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

react-wireflow

Package Overview
Dependencies
Maintainers
1
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-wireflow

Composable React node editor canvas with configurable nodes, connections, and inspector UI.

latest
npmnpm
Version
0.7.2
Version published
Weekly downloads
7
16.67%
Maintainers
1
Weekly downloads
 
Created
Source

react-wireflow

React components for building node-based workflow editors with TypeScript support.

npm version npm downloads bundle size status

Demo: https://trkbt10.github.io/react-wireflow/

Type-safe node definitions, customizable renderers, grid-based layouts, settings persistence, undo/redo, i18n.

Installation

Requires React ^19.2.0 (uses useEffectEvent).

npm install react-wireflow
import "react-wireflow/style.css";

Usage

import { NodeEditor, createNodeDefinition } from "react-wireflow";

const MyNode = createNodeDefinition({
  type: "my-node",
  displayName: "My Node",
  ports: [
    { id: "in", type: "input", position: "left" },
    { id: "out", type: "output", position: "right" },
  ],
});

function App() {
  const [data, setData] = useState({ nodes: {}, connections: {} });
  return <NodeEditor data={data} onDataChange={setData} nodeDefinitions={[MyNode]} />;
}

Changelog

Unreleased

  • Breaking: NodeRenderProps was removed. Use NodeRendererProps instead.

Custom ports and connections

Declare renderPort per port definition to override the visual while keeping editor interactions. The second argument renders the default dot, which you can keep for accessibility hitboxes or replace entirely. Always forward context.handlers and honor context.position (x, y, transform) for correct anchoring.

const CustomPorts = createNodeDefinition({
  type: "custom-ports",
  displayName: "Custom Ports",
  ports: [
    {
      id: "emit",
      type: "output",
      label: "Emit",
      position: "right",
      dataType: ["text", "html"],
      renderPort: (context, defaultRender) => {
        if (!context.position) return defaultRender();
        const { x, y, transform } = context.position;
        return (
          <div
            style={{ position: "absolute", left: x, top: y, transform: transform ?? "translate(-50%, -50%)" }}
            onPointerDown={context.handlers.onPointerDown}
            onPointerUp={context.handlers.onPointerUp}
            onPointerEnter={context.handlers.onPointerEnter}
            onPointerMove={context.handlers.onPointerMove}
            onPointerLeave={context.handlers.onPointerLeave}
            onPointerCancel={context.handlers.onPointerCancel}
            data-state={context.isConnectable ? "ready" : context.isHovered ? "hovered" : "idle"}
          >
            <span className="port-dot" />
            <span className="port-label">{context.port.label}</span>
          </div>
        );
      },
      renderConnection: (context, defaultRender) => {
        // Example: decorate connected lines; fall back to default during previews
        if (!context.connection) return defaultRender();
        return defaultRender();
      },
    },
  ],
});

PortRenderContext includes port, node, allNodes, allConnections, booleans (isConnecting, isConnectable, isCandidate, isHovered, isConnected), optional position, and pointer handlers you must preserve. ConnectionRenderContext provides phase, fromPort, toPort, their positions, selection/hover flags, and handlers for pointer/cxtmenu; use it to add badges or halos while keeping hit-testing intact. For dynamic ports, set instances, createPortId, and createPortLabel on the port definition (see src/examples/demos/custom/ports/port-playground for a complete playground).

Panels

Use defaultEditorGridLayers for built-in panels (canvas, inspector, statusbar):

import { defaultEditorGridConfig, defaultEditorGridLayers } from "react-wireflow";

<NodeEditor gridConfig={defaultEditorGridConfig} gridLayers={defaultEditorGridLayers} />

Or define custom layouts:

<NodeEditor
  gridConfig={{
    areas: [["canvas", "inspector"]],
    rows: [{ size: "1fr" }],
    columns: [{ size: "1fr" }, { size: "300px", resizable: true }],
  }}
  gridLayers={[
    { id: "canvas", component: <NodeCanvas />, gridArea: "canvas" },
    { id: "inspector", component: <InspectorPanel />, gridArea: "inspector" },
  ]}
/>

Add floating layers:

const layers = [
  ...defaultEditorGridLayers,
  {
    id: "minimap",
    component: <YourMinimap />,
    positionMode: "absolute",
    position: { right: 10, bottom: 10 },
    draggable: true,
  },
];

Drawer for mobile:

{ id: "panel", component: <MyPanel />, drawer: { placement: "right", open: isOpen } }

See examples for complete implementations.

Custom Inspector Panels

The Inspector panel can be customized at three levels:

1. Per-Node Custom Inspector (renderInspector)

Define renderInspector in your node definition to provide custom inspector content when that node type is selected:

import {
  createNodeDefinition,
  PropertySection,
  InspectorInput,
  InspectorDefinitionList,
  InspectorDefinitionItem,
  type InspectorRenderProps,
} from "react-wireflow";

type PersonNodeData = {
  name: string;
  email: string;
};

// Custom inspector component (function name starts with uppercase to use hooks)
function PersonInspector({ node, onUpdateNode }: InspectorRenderProps<PersonNodeData>) {
  const data = node.data ?? {};

  return (
    <PropertySection title="Person Details">
      <InspectorDefinitionList>
        <InspectorDefinitionItem label="Name">
          <InspectorInput
            value={data.name ?? ""}
            onChange={(e) => onUpdateNode({ data: { ...data, name: e.target.value } })}
          />
        </InspectorDefinitionItem>
        <InspectorDefinitionItem label="Email">
          <InspectorInput
            type="email"
            value={data.email ?? ""}
            onChange={(e) => onUpdateNode({ data: { ...data, email: e.target.value } })}
          />
        </InspectorDefinitionItem>
      </InspectorDefinitionList>
    </PropertySection>
  );
}

const PersonNode = createNodeDefinition({
  type: "person",
  displayName: "Person",
  renderInspector: PersonInspector,
});

InspectorRenderProps provides:

  • node - The selected node with typed data
  • onUpdateNode(updates) - Callback to update node properties
  • onDeleteNode() - Callback to delete the node
  • externalData, isLoadingExternalData, externalDataError - External data state
  • onUpdateExternalData(data) - Callback to update external data

2. Custom Inspector Tabs

Replace or extend the default tabs (Layers, Properties, Settings) by passing tabs to InspectorPanel:

import {
  InspectorPanel,
  InspectorLayersTab,
  InspectorPropertiesTab,
  InspectorSettingsTab,
  InspectorSection,
  PropertySection,
  type InspectorPanelTabConfig,
} from "react-wireflow";

// Custom tab component
const StatisticsTab = () => (
  <InspectorSection>
    <PropertySection title="Statistics">
      <p>Total nodes: 10</p>
    </PropertySection>
  </InspectorSection>
);

const customTabs: InspectorPanelTabConfig[] = [
  { id: "layers", label: "Layers", render: () => <InspectorLayersTab /> },
  { id: "properties", label: "Properties", render: () => <InspectorPropertiesTab /> },
  { id: "stats", label: "Stats", render: () => <StatisticsTab /> },
  { id: "settings", label: "Settings", render: () => <InspectorSettingsTab panels={[]} /> },
];

<InspectorPanel tabs={customTabs} />

3. Custom Settings Panels

Add panels to the Settings tab using settingsPanels:

import {
  InspectorPanel,
  InspectorDefinitionList,
  InspectorDefinitionItem,
  InspectorButton,
  type InspectorSettingsPanelConfig,
} from "react-wireflow";

const ExportPanel = () => {
  return (
    <InspectorDefinitionList>
      <InspectorDefinitionItem label="Format">
        <select><option>JSON</option><option>YAML</option></select>
      </InspectorDefinitionItem>
      <InspectorDefinitionItem label="">
        <InspectorButton onClick={() => alert("Exporting...")}>Export</InspectorButton>
      </InspectorDefinitionItem>
    </InspectorDefinitionList>
  );
};

const settingsPanels: InspectorSettingsPanelConfig[] = [
  { title: "Export Options", component: ExportPanel },
];

<InspectorPanel settingsPanels={settingsPanels} />

Available Inspector UI Components

Build consistent inspector UIs with these components:

Layout Components:

ComponentDescription
PropertySectionTitled section with header
InspectorSectionBasic section container
InspectorSectionTitleStandalone section title (H4)
InspectorDefinitionListSemantic <dl> wrapper
InspectorDefinitionItemLabel-value pair (<dt>/<dd>)
InspectorFieldField wrapper with label
PositionInputsGridGrid layout for position/size inputs

Form Inputs:

ComponentDescription
InspectorInputStyled text input
InspectorNumberInputNumber input with label
InspectorTextareaMulti-line text input
InspectorSelectStyled select dropdown
InspectorLabelStandalone form label
ReadOnlyFieldNon-editable display field

Interactive:

ComponentDescription
InspectorButtonButton (variants: primary, secondary, danger)
InspectorButtonGroupSegmented button control for options
InspectorShortcutButtonCompact button for shortcut settings
InspectorShortcutBindingValueKeyboard/pointer shortcut display

See the Custom Inspector example for a complete implementation.

Keywords

react

FAQs

Package last updated on 18 Dec 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts