Getting started
InstallationQuick startControlled and uncontrolled state
Entities
The entity modelSockets and widgetsEntity typesFrames and groupsText and commentsImages, video and models
Edges and connections
EdgesConnections
Data
EvaluationSaving and exportingUndo and redoLayout and alignmentThe flow storeGraph queriesClipboard and cloning
Interaction
Canvas and cameraSelectionKeyboardSelection toolbarContext menusDrawingDropping and pasting media
Styling
ThemingEntity stylingFonts and text renderingMinimap
API reference
KookieFlowThe ref instanceHooksUtilitiesTypes
StartEntitiesDataAPI
MarkdownOpenAIClaude

Selection

Select, move, resize, and delete entities and edges with the pointer, respond to clicks, and read or set the selection from code.

The selection is the set of entities and edges that the next action applies to. Dragging, deleting, aligning, and the selection toolbar all work on it. The canvas keeps the selection in its own store rather than in your entities array, and it doesn't report selection changes through onEntitiesChange or onEdgesChange.

Select with the pointer

A press and release that moves no more than 5 pixels is a click. A press that moves further is a drag.

GestureResult
Click an entitySelects that entity alone, and clears any selected edges.
Cmd-click or Ctrl-click an entityAdds the entity to the selection, or takes it out if it's already selected.
Drag across empty canvasDraws a box. When you release, the entities the box touches replace the selected entities.
Hold Cmd or Ctrl as you release a boxAdds the entities in the box to the current selection.
Click empty canvasClears the selection.

Cmd and Ctrl do the same thing on every platform. Shift doesn't add to the selection, because Shift locks the aspect ratio while you resize. The Keyboard chapter covers selecting with Cmd/Ctrl+A, the arrow keys, and Escape.

Some presses on an entity aren't clicks on it. A press on a socket starts a wire, a press on a widget such as a slider or a checkbox changes its value, a press on a video's control bar plays or scrubs it, and a press on a 3D model turns it, unless you press the strip along its top. None of these changes the selection.

Select edges

edgesSelectable controls whether a click can select an edge.

PropTypeDefaultMeaning
edgesSelectablebooleantrueLets a click select an edge.

Clicking an edge selects it alone and clears the selected entities. Cmd-click or Ctrl-click adds the edge to the selection, but doesn't take a selected edge back out.

A click is tested against entities first, so you can't click the part of an edge that passes under a node. A selection box selects entities only, never edges. With edgesSelectable set to false, a click on an edge counts as a click on empty canvas: it clears the selection and calls onPaneClick.

Move a selection

What moves depends on whether the entity you drag is already selected.

  • Drag a selected entity to move the whole selection with it.
  • Drag an unselected entity to select it alone and move only that entity.
  • Cmd-drag or Ctrl-drag an unselected entity to add it to the selection and move the whole selection.

Near the edge of the canvas the camera pans, so you can drag an entity to a part of the graph that's off screen. While you drag, alignment guides show when an edge or centre lines up with a nearby entity, and snapToGrid rounds the position to the grid instead. The Layout and alignment chapter covers both.

The canvas reports a move once, when you release. onEntitiesChange receives one position change for every entity in the drag, and nothing while the drag is in progress. If you hold the graph in your own state, apply those changes, or the next entities array you pass puts the entities back. The Controlled and uncontrolled state chapter explains why.

Resize an entity

Select an entity to resize it. Only selected entities have resize handles. While the pointer is over a selected entity, its four corners show a dot you can drag. The middle of each side also resizes, and the cursor changes when you're over one.

ModifierEffect while you drag a handle
Alt (Option on macOS)Resizes from the centre, so the opposite side moves too.
ShiftKeeps the aspect ratio. Images, videos, and models keep it without Shift, and Shift frees it.

The resizable field on an entity turns handles off. resizable: false removes them. { width: false } or { height: false } limits the resize to one axis, and you resize the entity from the middle of a side, because the corners don't respond. With snapToGrid on, the size changes in grid steps.

An entity can't shrink below a minimum size for its type.

EntityMinimum width × height
Node120 × 40, or taller when its sockets need the room
Frame100 × 60
Comment80 × 40
Text40 × 20
Image or video60 × 40
Model80 × 80

When you release, onEntitiesChange receives a dimensions change. It also receives a position change when the entity's top-left corner moved, which happens when you drag a top or left handle or resize from the centre.

Delete a selection

Press Delete or Backspace while the canvas has focus. The canvas removes the selected entities, the selected edges, and every edge attached to a selected entity, then clears the selection.

onEdgesChange receives a remove change for each of those edges first, then onEntitiesChange receives a remove change for each entity. With history on in useGraph, the whole delete is one undo step, as the Undo and redo chapter describes.

The keys do nothing while you're typing in a text entity or a node's field, because focus is in that field rather than on the canvas.

Respond to clicks

Three callbacks tell you what a click landed on.

PropTypeCalled when
onEntityClick(entity: Entity) => voidA click lands on an entity.
onEdgeClick(edge: Edge) => voidA click lands on an edge and edgesSelectable is true.
onPaneClick() => voidA click lands on empty canvas, or on an edge while edgesSelectable is false.

Each callback runs after the canvas has updated the selection, so the selection you read inside it already reflects the click. A drag calls none of them, and neither does a press on a socket, a widget, a resize handle, or a media control. A click inside the text you're editing moves the caret and doesn't call onEntityClick. After D picks up the pen, a press draws instead, as the Drawing chapter describes. There's no double-click or right-click prop; the Context menus chapter covers right-click.

This inspector shows the node you last clicked and closes when you click empty canvas.

editor.tsx
'use client';import { useState } from 'react';import { KookieFlow, useGraph, type Entity } from '@kushagradhawan/kookie-flow';export function Editor({ initialEntities }: { initialEntities: Entity[] }) {  const { entities, edges, onEntitiesChange, onEdgesChange, onConnect } = useGraph({ initialEntities });  const [inspectedId, setInspectedId] = useState<string | null>(null);  const inspected = entities.find((entity) => entity.id === inspectedId);  return (    <div style={{ display: 'flex', height: 480 }}>      <div style={{ flex: 1 }}>        <KookieFlow          entities={entities}          edges={edges}          onEntitiesChange={onEntitiesChange}          onEdgesChange={onEdgesChange}          onConnect={onConnect}          onEntityClick={(entity) => setInspectedId(entity.id)}          onPaneClick={() => setInspectedId(null)}        />      </div>      {inspected ? <aside style={{ width: 240 }}>{inspected.data.label ?? inspected.id}</aside> : null}    </div>  );}

The panel looks the entity up in your own state by id, rather than keeping the object the callback passed, so it shows the entity's latest data after later edits.

Read and set the selection from code

The ref returns the selection as it is at the moment you call it.

MethodSignatureReturns
getSelectedEntities() => Entity[]The selected entities, in the order of the entities array.
getSelectedEdges() => Edge[]The selected edges, in the order of the edges array.

To change the selection, use the flow store. useFlowStoreApi returns it, and it works in any component you render as a child of <KookieFlow>.

Store methodWhat it does
selectEntity(id, additive?)Selects one entity and clears the edges. With additive set to true, adds it instead.
selectEntities(ids)Replaces the selected entities. Selected edges stay.
toggleEntitySelection(id)Adds the entity, or takes it out if it's already selected.
selectEdge(id, additive?)Selects one edge and clears the entities. With additive set to true, adds it instead.
selectEdges(ids)Replaces the selected edges. Selected entities stay.
selectAll()Selects every entity. Selected edges stay.
deselectAll()Clears both.

A selection you set from code is still not reported through onEntitiesChange, and it isn't an undo step. The selected field on an entity you pass in doesn't select it either; only the store does.

This button, rendered inside the canvas, selects every image on the board.

select-images.tsx
import { useFlowStoreApi } from '@kushagradhawan/kookie-flow';export function SelectImages() {  const store = useFlowStoreApi();  const selectImages = () => {    const { entities, selectEntities } = store.getState();    selectEntities(entities.filter((entity) => entity.type === 'image').map((entity) => entity.id));  };  return (    <button      style={{ position: 'absolute', top: 12, left: 12, pointerEvents: 'auto' }}      // Without this, the canvas treats the press as a click on the graph behind the button.      onPointerDown={(event) => event.stopPropagation()}      onClick={selectImages}    >      Select images    </button>  );}

Render it as <KookieFlow {...props}><SelectImages /></KookieFlow>. The canvas draws its children in a layer over the graph that has pointer-events: none, so a control inside needs pointerEvents: 'auto' to be pressable. It also needs to stop pointerdown from bubbling. Otherwise the canvas captures the pointer, treats the press as a click on whatever lies underneath, and your button's onClick never runs.

To show something that changes with the selection, read selectedEntityIds with useFlowStore, as the chapter on the flow store describes. The selection doesn't change while a drag is in progress, so a component that reads it doesn't render while entities move.

Getting started
  • Installation
  • Quick start
  • Controlled and uncontrolled state
Entities
  • The entity model
  • Sockets and widgets
  • Entity types
  • Frames and groups
  • Text and comments
  • Images, video and models
Edges and connections
  • Edges
  • Connections
Data
  • Evaluation
  • Saving and exporting
  • Undo and redo
  • Layout and alignment
  • The flow store
  • Graph queries
  • Clipboard and cloning
Interaction
  • Canvas and camera
  • Selection
  • Keyboard
  • Selection toolbar
  • Context menus
  • Drawing
  • Dropping and pasting media
Styling
  • Theming
  • Entity styling
  • Fonts and text rendering
  • Minimap
API reference
  • KookieFlow
  • The ref instance
  • Hooks
  • Utilities
  • Types
Reference
  • Live demo
  • llms.txt
  • GitHub
MIT licensed. © 2026 Kushagra Dhawan.
On this page
  • Select with the pointer
  • Select edges
  • Move a selection
  • Resize an entity
  • Delete a selection
  • Respond to clicks
  • Read and set the selection from code