The Element Binding System manages the attachment relationships between elements in Excalidraw. This system handles two primary binding types: arrow-to-element bindings (connecting arrow endpoints to shapes) and text-to-container bindings (attaching text elements inside shapes). The system maintains binding metadata, detects eligible binding targets, updates bound elements during transformations, and repairs broken bindings during data restoration.
For information about element creation and lifecycle, see Element Types and Creation. For details on element transformations, see Element Properties and Transformations.
Sources: packages/element/src/binding.ts1-100 packages/excalidraw/data/restore.ts52-86
| Structure | Properties | Usage |
|---|---|---|
PointBinding | elementId, focus, gap | Standard arrow-to-element binding |
FixedPointBinding | extends PointBinding + fixedPoint: [x, y] | Elbow arrow bindings with fixed attachment points |
boundElements | Array of {id: string, type: 'arrow'|'text'} | Tracks elements bound to a container |
containerId | string | null | Text element reference to its container |
Sources: packages/element/src/binding.ts70-85 packages/excalidraw/data/restore.ts60-66
Sources: packages/element/src/binding.ts97-105 packages/element/src/binding.ts574-658
The PointBinding structure stores three key parameters:
elementId: The ID of the element being bound tofocus: Normalized position along the element's border (-1 to 1), calculated by determineFocusDistance() packages/element/src/binding.ts896-980gap: Distance from arrow endpoint to element border, constrained by maxBindingGap() packages/element/src/binding.ts982-992For elbow arrows, FixedPointBinding adds:
fixedPoint: Absolute [x, y] coordinates of the attachment point, calculated by calculateFixedPointForElbowArrowBinding() packages/element/src/binding.ts1102-1249Sources: packages/element/src/binding.ts480-528 packages/element/src/binding.ts704-734
The system ensures bidirectional consistency:
startBinding/endBinding with target element IDboundElements arraySources: packages/element/src/binding.ts124-217 packages/element/src/binding.ts480-528 packages/element/src/binding.ts560-572
| Drag Type | Strategy | Implementation |
|---|---|---|
| Arrow endpoints | Re-evaluate bindings for dragged endpoints | getBindingStrategyForDraggingArrowEndpoints() packages/element/src/binding.ts243-279 |
| Arrow shaft | Keep bindings if still close to original elements | getBindingStrategyForDraggingArrowOrJoints() packages/element/src/binding.ts281-322 |
| Elbow arrows | No rebinding during drag | Returns ["keep", "keep"] packages/element/src/binding.ts289-291 |
Sources: packages/element/src/binding.ts324-353
Sources: packages/excalidraw/actions/actionBoundText.tsx1-221 packages/element/src/textElement.ts1-50
The text binding lifecycle follows these steps:
Creation: actionBindText validates that exactly one text element and one compatible container are selected packages/excalidraw/actions/actionBoundText.tsx108-138
Binding: Sets textElement.containerId and adds text reference to container.boundElements packages/excalidraw/actions/actionBoundText.tsx155-167
Positioning: computeBoundTextPosition() calculates centered position within container packages/element/src/textElement.ts400-500
Resizing: redrawTextBoundingBox() adjusts text dimensions to fit container with padding packages/element/src/textElement.ts600-700
Maintenance: handleBindTextResize() updates container dimensions when text changes packages/element/src/textElement.ts800-900
Sources: packages/excalidraw/actions/actionBoundText.tsx53-105 packages/excalidraw/actions/actionBoundText.tsx139-180
Sources: packages/excalidraw/actions/actionBoundText.tsx222-300 packages/element/src/textElement.ts200-350
The getHoveredElementForBinding() function determines which element (if any) should be bound to at a given pointer position:
Sources: packages/element/src/binding.ts574-658 packages/element/src/binding.ts660-702
The bindingBorderTest() function checks if a point is within the binding activation distance of an element's border:
FIXED_BINDING_DISTANCE (5 pixels) packages/element/src/binding.ts107distanceToElement() for precise border distanceSources: packages/element/src/binding.ts1251-1290 packages/element/src/distance.ts29-53
Bound elements must be updated when their bound targets change:
Sources: packages/element/src/binding.ts736-859 packages/element/src/binding.ts861-878
The updateBoundElements() function follows this algorithm for each bound arrow:
Check if update needed: doesNeedUpdate() verifies the arrow binds to the changed element packages/element/src/binding.ts880-888
Adjust gap for scaling: If container was resized, maybeCalculateNewGapWhenScaling() proportionally adjusts binding gap packages/element/src/binding.ts1292-1316
Calculate new point: updateBoundPoint() computes new arrow endpoint position based on updated binding packages/element/src/binding.ts1318-1377
Update arrow geometry: LinearElementEditor.movePoints() updates arrow points and bound text position packages/element/src/binding.ts845-852
Handle bound text: If arrow has bound text, handleBindTextResize() repositions it packages/element/src/binding.ts854-857
Sources: packages/element/src/binding.ts738-858
When multiple elements are updated simultaneously (e.g., during group transformations), the system optimizes binding updates:
simultaneouslyUpdatedElementIds set identifies elements being moved together packages/element/src/binding.ts752-754Sources: packages/element/src/binding.ts890-894 packages/element/src/binding.ts742-763
Elbow arrows use FixedPointBinding with absolute attachment coordinates instead of relative focus values:
Sources: packages/element/src/binding.ts1102-1249 packages/excalidraw/data/restore.ts124-156
| Aspect | Standard PointBinding | FixedPointBinding (Elbow) |
|---|---|---|
| Position storage | Relative focus (-1 to 1) | Absolute fixedPoint [x, y] |
| Attachment | Calculated from focus along border | Fixed to specific coordinates |
| Updating | Recalculate from focus on changes | Update routing but keep fixedPoint |
| Snapping | Not snapped | Snapped to grid/edges/midpoints |
| Repair | Use repairBinding() | Use normalizeFixedPoint() |
Sources: packages/element/src/binding.ts469-478 packages/excalidraw/data/restore.ts136-148
Elbow arrows have special binding behavior:
"keep" strategy packages/element/src/binding.ts288-291validateElbowPoints() checks for valid routing packages/excalidraw/data/restore.ts631-650fixedSegments for routing constraints packages/excalidraw/data/restore.ts390-396Sources: packages/element/src/binding.ts281-291 packages/excalidraw/data/restore.ts628-693
Sources: packages/excalidraw/data/restore.ts527-694
The restoration system applies these repairs:
Container Element Repair (repairContainerElement):
boundElements array packages/excalidraw/data/restore.ts436-443containerId on text elements that are missing it packages/excalidraw/data/restore.ts452-460Bound Element Repair (repairBoundElement):
containerId if container doesn't exist packages/excalidraw/data/restore.ts487-490boundElements if missing packages/excalidraw/data/restore.ts496-506Linear Element Repair:
startBinding/endBinding if target element doesn't exist packages/excalidraw/data/restore.ts611-625Sources: packages/excalidraw/data/restore.ts420-625
During restoration, legacy binding formats are migrated:
boundElementIds → boundElements: Old format stored only IDs, new format includes type packages/excalidraw/data/restore.ts214-216focus property defaults to 0 packages/excalidraw/data/restore.ts134FixedPointBinding packages/excalidraw/data/restore.ts136-147normalizeFixedPoint() ensures valid coordinates packages/excalidraw/data/restore.ts143Sources: packages/excalidraw/data/restore.ts124-156 packages/excalidraw/data/restore.ts214-216
Several action handlers interact with the binding system:
| Action | Binding Interaction | Implementation |
|---|---|---|
actionFlipHorizontal/Vertical | Rebinds arrows after flip transformation | packages/excalidraw/actions/actionFlip.ts161-167 |
actionBindText | Creates text-to-container binding | packages/excalidraw/actions/actionBoundText.tsx108-180 |
actionUnbindText | Removes text-to-container binding | packages/excalidraw/actions/actionBoundText.tsx53-105 |
changeProperty | Updates bound elements after style changes | packages/excalidraw/actions/actionProperties.tsx274-281 |
Sources: packages/excalidraw/actions/actionFlip.ts86-207 packages/excalidraw/actions/actionBoundText.tsx1-221
| Constant | Value | Purpose |
|---|---|---|
FIXED_BINDING_DISTANCE | 5 | Pixel threshold for binding detection packages/element/src/binding.ts107 |
BINDING_HIGHLIGHT_THICKNESS | 10 | Visual highlight thickness for bind preview packages/element/src/binding.ts108 |
BOUND_TEXT_PADDING | 20 | Minimum padding for text inside containers packages/common/src/constants.ts |
Sources: packages/element/src/binding.ts107-108 packages/excalidraw/actions/actionBoundText.tsx2-8
Refresh this wiki