From 18514e118fc9a43e2bc80e55639db8beb1d9707a Mon Sep 17 00:00:00 2001 From: Ze-Zheng Wu Date: Sat, 5 Jul 2025 18:11:27 +0800 Subject: [PATCH 1/3] fix(react): respect custom default node type when falling back from unknown node type --- .changeset/forty-dryers-cry.md | 5 +++ examples/react/src/App/routes.ts | 6 +++ .../examples/DefaultNodeOverwrite/index.tsx | 41 +++++++++++++++++++ .../src/components/NodeWrapper/index.tsx | 2 +- 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 .changeset/forty-dryers-cry.md create mode 100644 examples/react/src/examples/DefaultNodeOverwrite/index.tsx diff --git a/.changeset/forty-dryers-cry.md b/.changeset/forty-dryers-cry.md new file mode 100644 index 00000000..618ce567 --- /dev/null +++ b/.changeset/forty-dryers-cry.md @@ -0,0 +1,5 @@ +--- +'@xyflow/react': patch +--- + +Fix node fallback to respect custom default node type when unknown node type is encountered diff --git a/examples/react/src/App/routes.ts b/examples/react/src/App/routes.ts index 7bbe7890..031f38c6 100644 --- a/examples/react/src/App/routes.ts +++ b/examples/react/src/App/routes.ts @@ -9,6 +9,7 @@ import ControlledViewport from '../examples/ControlledViewport'; import CustomConnectionLine from '../examples/CustomConnectionLine'; import CustomMiniMapNode from '../examples/CustomMiniMapNode'; import CustomNode from '../examples/CustomNode'; +import DefaultNodeOverwrite from '../examples/DefaultNodeOverwrite'; import DefaultNodes from '../examples/DefaultNodes'; import DragHandle from '../examples/DragHandle'; import DragNDrop from '../examples/DragNDrop'; @@ -129,6 +130,11 @@ const routes: IRoute[] = [ path: 'custom-node', component: CustomNode, }, + { + name: 'Default Node Overwrite', + path: 'default-node-overwrite', + component: DefaultNodeOverwrite, + }, { name: 'Default Nodes', path: 'default-nodes', diff --git a/examples/react/src/examples/DefaultNodeOverwrite/index.tsx b/examples/react/src/examples/DefaultNodeOverwrite/index.tsx new file mode 100644 index 00000000..051f4ad4 --- /dev/null +++ b/examples/react/src/examples/DefaultNodeOverwrite/index.tsx @@ -0,0 +1,41 @@ +import { ReactFlow, Node, ReactFlowProvider, Background, BackgroundVariant, NodeProps } from '@xyflow/react'; + +const initialNodes: Node[] = [ + { + id: '1', + data: { label: 'Node 1' }, + position: { x: 250, y: 5 }, + className: 'light', + }, + { + id: '2', + data: { label: 'Node 2' }, + type: 'unregistered', + position: { x: 100, y: 100 }, + className: 'light', + }, +]; + +const CustomNode = (_: NodeProps) => { + return
Custom node
; +}; + +const nodeTypes = { + default: CustomNode, +}; + +const DefaultNodes = () => { + return ( + + + + ); +}; + +export default function App() { + return ( + + + + ); +} diff --git a/packages/react/src/components/NodeWrapper/index.tsx b/packages/react/src/components/NodeWrapper/index.tsx index dcab1536..bfac2087 100644 --- a/packages/react/src/components/NodeWrapper/index.tsx +++ b/packages/react/src/components/NodeWrapper/index.tsx @@ -58,7 +58,7 @@ export function NodeWrapper({ if (NodeComponent === undefined) { onError?.('003', errorMessages['error003'](nodeType)); nodeType = 'default'; - NodeComponent = builtinNodeTypes.default; + NodeComponent = nodeTypes?.['default'] || builtinNodeTypes.default; } const isDraggable = !!(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined')); From 680f6e8b2d6cc86c05131b76b0245ab441b83318 Mon Sep 17 00:00:00 2001 From: Ze-Zheng Wu Date: Sat, 5 Jul 2025 18:23:33 +0800 Subject: [PATCH 2/3] chore: change component name for clarity --- examples/react/src/examples/DefaultNodeOverwrite/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/react/src/examples/DefaultNodeOverwrite/index.tsx b/examples/react/src/examples/DefaultNodeOverwrite/index.tsx index 051f4ad4..8c8c0e49 100644 --- a/examples/react/src/examples/DefaultNodeOverwrite/index.tsx +++ b/examples/react/src/examples/DefaultNodeOverwrite/index.tsx @@ -24,7 +24,7 @@ const nodeTypes = { default: CustomNode, }; -const DefaultNodes = () => { +const DefaultNodeOverwrite = () => { return ( @@ -35,7 +35,7 @@ const DefaultNodes = () => { export default function App() { return ( - + ); } From ab05d008d949c98124578e99e6a6e4c86a16f629 Mon Sep 17 00:00:00 2001 From: Ze-Zheng Wu Date: Sat, 5 Jul 2025 19:31:55 +0800 Subject: [PATCH 3/3] fix(react): also respect custom default edge type in fallback logic --- .changeset/purple-donuts-fail.md | 5 ++ examples/react/src/App/routes.ts | 6 ++ .../examples/DefaultEdgeOverwrite/index.tsx | 69 +++++++++++++++++++ .../src/components/EdgeWrapper/index.tsx | 2 +- 4 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 .changeset/purple-donuts-fail.md create mode 100644 examples/react/src/examples/DefaultEdgeOverwrite/index.tsx diff --git a/.changeset/purple-donuts-fail.md b/.changeset/purple-donuts-fail.md new file mode 100644 index 00000000..af8df925 --- /dev/null +++ b/.changeset/purple-donuts-fail.md @@ -0,0 +1,5 @@ +--- +'@xyflow/react': patch +--- + +Fix edge fallback to respect custom default edge type when unknown edge type is encountered. diff --git a/examples/react/src/App/routes.ts b/examples/react/src/App/routes.ts index 031f38c6..4c003ea5 100644 --- a/examples/react/src/App/routes.ts +++ b/examples/react/src/App/routes.ts @@ -9,6 +9,7 @@ import ControlledViewport from '../examples/ControlledViewport'; import CustomConnectionLine from '../examples/CustomConnectionLine'; import CustomMiniMapNode from '../examples/CustomMiniMapNode'; import CustomNode from '../examples/CustomNode'; +import DefaultEdgeOverwrite from '../examples/DefaultEdgeOverwrite'; import DefaultNodeOverwrite from '../examples/DefaultNodeOverwrite'; import DefaultNodes from '../examples/DefaultNodes'; import DragHandle from '../examples/DragHandle'; @@ -135,6 +136,11 @@ const routes: IRoute[] = [ path: 'default-node-overwrite', component: DefaultNodeOverwrite, }, + { + name: 'Default Edge Overwrite', + path: 'default-edge-overwrite', + component: DefaultEdgeOverwrite, + }, { name: 'Default Nodes', path: 'default-nodes', diff --git a/examples/react/src/examples/DefaultEdgeOverwrite/index.tsx b/examples/react/src/examples/DefaultEdgeOverwrite/index.tsx new file mode 100644 index 00000000..47c7baaf --- /dev/null +++ b/examples/react/src/examples/DefaultEdgeOverwrite/index.tsx @@ -0,0 +1,69 @@ +import { + ReactFlow, + Node, + Edge, + ReactFlowProvider, + Background, + BackgroundVariant, + EdgeProps, + getBezierPath, +} from '@xyflow/react'; + +const initialNodes: Node[] = [ + { + id: '1', + data: { label: 'Node 1' }, + position: { x: 250, y: 5 }, + className: 'light', + }, + { + id: '2', + data: { label: 'Node 2' }, + position: { x: 100, y: 100 }, + className: 'light', + }, +]; + +const initialEdges: Edge[] = [ + { + id: 'e1-2', + source: '1', + target: '2', + type: 'unregistered', // This will fallback to custom default + }, +]; + +const CustomEdge = ({ sourceX, sourceY, targetX, targetY }: EdgeProps) => { + const [edgePath] = getBezierPath({ + sourceX, + sourceY, + targetX, + targetY, + }); + + return ( + <> + + + ); +}; + +const edgeTypes = { + default: CustomEdge, +}; + +const DefaultEdgeOverwrite = () => { + return ( + + + + ); +}; + +export default function App() { + return ( + + + + ); +} diff --git a/packages/react/src/components/EdgeWrapper/index.tsx b/packages/react/src/components/EdgeWrapper/index.tsx index 403ef08d..db85bc08 100644 --- a/packages/react/src/components/EdgeWrapper/index.tsx +++ b/packages/react/src/components/EdgeWrapper/index.tsx @@ -46,7 +46,7 @@ export function EdgeWrapper({ if (EdgeComponent === undefined) { onError?.('011', errorMessages['error011'](edgeType)); edgeType = 'default'; - EdgeComponent = builtinEdgeTypes.default; + EdgeComponent = edgeTypes?.['default'] || builtinEdgeTypes.default; } const isFocusable = !!(edge.focusable || (edgesFocusable && typeof edge.focusable === 'undefined'));