Skip to Content
ExamplesNodes

Delete Middle Node

This example shows you how to recover deleted edges when you remove a node from the middle of a chain. In other words, if we have three nodes connected in sequence - a->b->c - and we deleted the middle node b, this example shows you how to end up with the graph a->c.

To achieve this, we need to make use of a few bits:

All together, this allows us to take all the nodes connected to the deleted node, and reconnect them to any nodes the deleted node was connected to.

/* xyflow theme files. Delete these to start from our base */ .react-flow { --xy-background-color: #f7f9fb; /* Custom Variables */ --xy-theme-selected: #f57dbd; --xy-theme-hover: #c5c5c5; --xy-theme-edge-hover: black; --xy-theme-color-focus: #e8e8e8; /* Built-in Variables see https://reactflow.dev/learn/customization/theming */ --xy-node-border-default: 1px solid #ededed; --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px #00000005, 0px 3.54px 4.55px 0px #0000000d, 0px 0.51px 1.01px 0px #0000001a; --xy-node-border-radius-default: 8px; --xy-handle-background-color-default: #ffffff; --xy-handle-border-color-default: #aaaaaa; --xy-edge-label-color-default: #505050; --xy-node-background-color-default: #ffffff; --xy-theme-panel-bg: #ffffff; --xy-theme-panel-text: #111827; --xy-theme-resize-handle-bg: #ffffff; --xy-theme-focus-border: #d9d9d9; --xy-theme-muted-bg: #f3f4f6; --xy-theme-subtle-border: #d1d5db; } .react-flow.dark { --xy-background-color: #101012; --xy-node-background-color-default: #1a1b1e; --xy-node-border-default: 1px solid #303238; --xy-handle-background-color-default: #141519; --xy-handle-border-color-default: #5a5d66; --xy-edge-label-color-default: #e6e7ea; --xy-theme-hover: #7a7d86; --xy-theme-edge-hover: #f3f4f6; --xy-theme-panel-bg: #141519; --xy-theme-panel-text: #f3f4f6; --xy-theme-resize-handle-bg: #141519; --xy-theme-focus-border: #5f6470; --xy-theme-muted-bg: #24262c; --xy-theme-subtle-border: #3f434c; --xy-node-boxshadow-default: none; --xy-theme-color-focus: #4b4e57; --color-background: #1a1b1e; --color-hover-bg: #24262c; --color-disabled: #8b9099; } .react-flow { background-color: var(--xy-background-color); } /* Customizing Default Theming */ .react-flow__node { box-shadow: var(--xy-node-boxshadow-default); border-radius: var(--xy-node-border-radius-default); background-color: var(--xy-node-background-color-default); display: flex; justify-content: center; align-items: center; text-align: center; padding: 10px; font-size: 12px; flex-direction: column; border: var(--xy-node-border-default); color: var(--xy-node-color, var(--xy-node-color-default)); } .react-flow__node.selectable:focus { box-shadow: 0px 0px 0px 4px var(--xy-theme-color-focus); border-color: var(--xy-theme-focus-border); } .react-flow__node.selectable:focus:active { box-shadow: var(--xy-node-boxshadow-default); } .react-flow__node.selectable:hover, .react-flow__node.draggable:hover { border-color: var(--xy-theme-hover); } .react-flow__node.selectable.selected { border-color: var(--xy-theme-selected); box-shadow: var(--xy-node-boxshadow-default); } .react-flow__node-group { background-color: rgba(207, 182, 255, 0.4); border-color: #9e86ed; } .react-flow.dark .react-flow__node-group { background-color: rgba(123, 107, 148, 0.22); border-color: #7b6b94; } .react-flow__edge.selectable:hover .react-flow__edge-path, .react-flow__edge.selectable.selected .react-flow__edge-path { stroke: var(--xy-theme-edge-hover); } .react-flow__handle { background-color: var(--xy-handle-background-color-default); } .react-flow__handle.connectionindicator:hover { pointer-events: all; border-color: var(--xy-theme-edge-hover); background-color: var(--xy-handle-background-color-default); } .react-flow__handle.connectionindicator:focus, .react-flow__handle.connectingfrom, .react-flow__handle.connectingto { border-color: var(--xy-theme-edge-hover); } .react-flow__node-resizer { border-radius: 0; border: none; } .react-flow__resize-control.handle { background-color: var(--xy-theme-resize-handle-bg); border-color: #9e86ed; border-radius: 0; width: 5px; height: 5px; } /* Custom Example CSS - This CSS is to improve the example experience. You can remove it if you want to use the default styles. New Theme Classes: .xy-theme__button - Styles for buttons. .xy-theme__input - Styles for text inputs. .xy-theme__checkbox - Styles for checkboxes. .xy-theme__select - Styles for dropdown selects. .xy-theme__label - Styles for labels. Use these classes to apply consistent theming across your components. */ :root { --color-primary: #ff0073; --color-background: #fefefe; --color-hover-bg: #f6f6f6; --color-disabled: #76797e; } .xy-theme__button-group { display: flex; align-items: center; .xy-theme__button:first-child { border-radius: 100px 0 0 100px; } .xy-theme__button:last-child { border-radius: 0 100px 100px 0; margin: 0; } } /* Custom Button Styling */ .xy-theme__button { display: inline-flex; align-items: center; justify-content: center; height: 2.5rem; padding: 0 1rem; border-radius: 100px; border: 1px solid var(--color-primary); background-color: var(--color-background); color: var(--color-primary); transition: background-color 0.2s ease, border-color 0.2s ease; box-shadow: var(--xy-node-boxshadow-default); cursor: pointer; } .xy-theme__button.active { background-color: var(--color-primary); color: white; border-color: var(--color-primary); } .xy-theme__button.active:hover, .xy-theme__button.active:active { background-color: var(--color-primary); opacity: 0.9; } .xy-theme__button:hover { background-color: var(--xy-controls-button-background-color-hover-default); } .xy-theme__button:active { background-color: var(--color-hover-bg); } .xy-theme__button:disabled { color: var(--color-disabled); opacity: 0.8; cursor: not-allowed; border: 1px solid var(--color-disabled); } .xy-theme__button > span { margin-right: 0.2rem; } /* Add gap between adjacent buttons */ .xy-theme__button + .xy-theme__button { margin-left: 0.3rem; } /* Example Input Styling */ .xy-theme__input { padding: 0.5rem 0.75rem; border: 1px solid var(--color-primary); border-radius: 7px; background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; font-size: 1rem; color: var(--xy-theme-panel-text); } .xy-theme__input:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } /* Specific Checkbox Styling */ .xy-theme__checkbox { appearance: none; -webkit-appearance: none; width: 1.25rem; height: 1.25rem; border-radius: 7px; border: 2px solid var(--color-primary); background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; cursor: pointer; display: inline-block; vertical-align: middle; margin-right: 0.5rem; } .xy-theme__checkbox:checked { background-color: var(--color-primary); border-color: var(--color-primary); } .xy-theme__checkbox:focus { outline: none; box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } /* Dropdown Styling */ .xy-theme__select { padding: 0.5rem 0.75rem; border: 1px solid var(--color-primary); border-radius: 50px; background-color: var(--color-background); transition: background-color 0.2s ease, border-color 0.2s ease; font-size: 1rem; color: var(--xy-theme-panel-text); margin-right: 0.5rem; box-shadow: var(--xy-node-boxshadow-default); } .xy-theme__select:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(255, 0, 115, 0.3); } .xy-theme__label { margin-top: 10px; margin-bottom: 3px; display: inline-block; color: var(--xy-theme-panel-text); }

Although this example is less than 20 lines of code there’s quite a lot to digest. Let’s break some of it down:

  • Our onNodesDelete callback is called with one argument - deleted - that is an array of every node that was just deleted. If you select an individual node and press the delete key, deleted will contain just that node, but if you make a selection all the nodes in that selection will be in deleted.

  • We create a new array of edges - remainingEdges - that contains all the edges in the flow that have nothing to do with the node(s) we just deleted.

  • We create another array of edges by flatMapping over the array of incomers. These are nodes that were connected to the deleted node as a source. For each of these nodes, we create a new edge that connects to each node in the array of outgoers. These are nodes that were connected to the deleted node as a target.

For brevity, we’re using object destructuring while at the same time renaming the variable bound (e.g. ({ id: source }) => ...) destructures the id property of the object and binds it to a new variable called source) but you don’t need to do this

Quick Reference

Last updated on