# Drag nodes safely

## What you will make

Drag a card beyond its painted body and keep its connection attached until the pointer ends.

## How it works

The card captures one pointer ID on pointer down. [`useRef`](/reference/hooks/use-ref/) stores that gesture synchronously between native events, while rendered state remains frame-coalesced.

## Build the change

Record root-logical pointer and workspace node origins. Each move uses an immutable functional update. [Event types](/reference/types/events/) expose capture, cancel, and loss notifications.

```tsx
const dragSession = useRef<NodeDragSession | null>(null);

const onPointerDown = (node: DiagramNode, event: PibblPointerEvent) => {
  const pointerStart = rootToWorkspace(event);
  event.currentTarget.setPointerCapture(event.pointerId);
  dragSession.current = {
    kind: "node",
    pointerId: event.pointerId,
    nodeId: node.id,
    pointerStart,
    nodeStart: { x: node.x, y: node.y },
  };
};

const onPointerMove = (event: PibblPointerEvent) => {
  const session = dragSession.current;
  if (!session || session.pointerId !== event.pointerId) return;
  const point = rootToWorkspace(event);
  setNodes((current) =>
    current.map((node) =>
      node.id === session.nodeId
        ? {
            ...node,
            x: session.nodeStart.x + point.x - session.pointerStart.x,
            y: session.nodeStart.y + point.y - session.pointerStart.y,
          }
        : node,
    ),
  );
};
```

Matching the pointer ID prevents a second pointer from stealing the drag. Capturing before the pointer leaves the card prevents outside moves from disappearing; pointer up, cancellation, and capture loss all clear the same session so a stale owner cannot move a node later.

## Predict and try

Predict why a move before the next paint still has a current owner. Then clamp positions to x `-40..1232` and y `-40..816` for generous panning overscan.

## Check your result

Idea moves outside its original card and its edge follows. Release, `pointercancel`, and `lostpointercapture` clear the session so later moves cannot change it.

## What you learned

Capture is per pointer ID, and refs retain synchronous gesture ownership. See [Manage state and lifecycle](/guides/state-and-lifecycle/).

[Open the interactive workbench](/playground/#/workbench/diagram-editor-dragging)

## Implementation guidance for agents

Read the [Input, focus, and native HTML companion](/agents/topics/input/) for ownership, adaptation, failure modes, and verification. [Agent start](/agents/) provides the version-selection workflow.

## Documentation version

Documentation built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.
