Skip to main content

Element Utilities

Utility functions for creating, mutating, and working with Excalidraw elements.

Import

import {
  // Element creation
  newElement,
  newTextElement,
  newLinearElement,
  newArrowElement,
  newImageElement,
  newFrameElement,
  
  // Element mutation
  mutateElement,
  newElementWith,
  bumpVersion,
  
  // Element queries
  getNonDeletedElements,
  getSceneVersion,
  hashElementsVersion,
  hashString,
  
  // Element bounds
  getElementAbsoluteCoords,
  getElementBounds,
  getCommonBounds,
  getVisibleSceneBounds,
  
  // Bounding box utilities
  elementsOverlappingBBox,
  isElementInsideBBox,
  elementPartiallyOverlapsWithOrContainsBBox,
  
  // Data utilities
  getDataURL,
  
  // Library utilities
  parseLibraryTokensFromUrl,
  useHandleLibrary,
  
  // Text utilities
  setCustomTextMetricsProvider,
  
  // Types
  CaptureUpdateAction,
} from "@excalidraw/excalidraw";

Element Creation

newElement

Creates a new generic Excalidraw element.
function newElement(
  opts: {
    type: ExcalidrawGenericElement["type"];
  } & ElementConstructorOpts
): NonDeleted<ExcalidrawGenericElement>
opts.type
string
required
Element type (e.g., “rectangle”, “ellipse”, “diamond”)
opts.x
number
required
X coordinate of the element
opts.y
number
required
Y coordinate of the element
opts.width
number
default:"0"
Width of the element
opts.height
number
default:"0"
Height of the element
opts.angle
Radians
default:"0"
Rotation angle in radians
opts.strokeColor
string
Stroke color for the element
opts.backgroundColor
string
Background fill color
opts.fillStyle
string
Fill style (“solid”, “hachure”, “cross-hatch”)
opts.strokeWidth
number
Width of the stroke
opts.roughness
number
Roughness level for hand-drawn appearance
opts.opacity
number
Opacity value (0-100)
element
ExcalidrawElement
The newly created element with all properties initialized
Example:
import { newElement } from "@excalidraw/excalidraw";

const rectangle = newElement({
  type: "rectangle",
  x: 100,
  y: 100,
  width: 200,
  height: 150,
  strokeColor: "#000000",
  backgroundColor: "#ffffff",
  fillStyle: "hachure",
  strokeWidth: 2,
  roughness: 1,
  opacity: 100,
});

newTextElement

Creates a new text element with automatic dimension calculations.
function newTextElement(
  opts: {
    text: string;
    fontSize?: number;
    fontFamily?: FontFamilyValues;
    textAlign?: TextAlign;
    verticalAlign?: VerticalAlign;
    containerId?: string | null;
    lineHeight?: number;
    autoResize?: boolean;
  } & ElementConstructorOpts
): NonDeleted<ExcalidrawTextElement>
opts.text
string
required
Text content to display
opts.fontSize
number
Font size in pixels
opts.fontFamily
FontFamilyValues
Font family identifier (FONT_FAMILY constant)
opts.textAlign
'left' | 'center' | 'right'
Horizontal text alignment
opts.verticalAlign
'top' | 'middle'
Vertical text alignment
opts.containerId
string | null
ID of container element (for bound text)
opts.autoResize
boolean
default:"true"
Whether text should auto-resize
Example:
import { newTextElement, FONT_FAMILY } from "@excalidraw/excalidraw";

const textElement = newTextElement({
  text: "Hello, World!",
  x: 100,
  y: 100,
  fontSize: 20,
  fontFamily: FONT_FAMILY.Virgil,
  textAlign: "center",
  strokeColor: "#000000",
});

newLinearElement

Creates a line or arrow element.
function newLinearElement(
  opts: {
    type: "line" | "arrow";
    points?: LocalPoint[];
  } & ElementConstructorOpts
): NonDeleted<ExcalidrawLinearElement>
opts.type
'line' | 'arrow'
required
Type of linear element
opts.points
LocalPoint[]
Array of points [x, y] defining the line path
Example:
import { newLinearElement } from "@excalidraw/excalidraw";

const line = newLinearElement({
  type: "line",
  x: 100,
  y: 100,
  points: [
    [0, 0],
    [100, 50],
    [200, 100],
  ],
  strokeColor: "#000000",
});

newArrowElement

Creates an arrow element with optional arrowheads.
function newArrowElement(
  opts: {
    type: "arrow";
    startArrowhead?: Arrowhead | null;
    endArrowhead?: Arrowhead | null;
    points?: LocalPoint[];
    elbowed?: boolean;
  } & ElementConstructorOpts
): NonDeleted<ExcalidrawArrowElement>
opts.startArrowhead
Arrowhead | null
Arrowhead style for start point (“arrow”, “dot”, “bar”, etc.)
opts.endArrowhead
Arrowhead | null
Arrowhead style for end point
opts.elbowed
boolean
Whether the arrow should use elbow routing
Example:
import { newArrowElement } from "@excalidraw/excalidraw";

const arrow = newArrowElement({
  type: "arrow",
  x: 100,
  y: 100,
  points: [
    [0, 0],
    [200, 0],
  ],
  startArrowhead: null,
  endArrowhead: "arrow",
  strokeColor: "#000000",
});

newImageElement

Creates an image element.
function newImageElement(
  opts: {
    type: "image";
    fileId?: string | null;
    status?: "pending" | "saved" | "error";
    scale?: [number, number];
  } & ElementConstructorOpts
): NonDeleted<ExcalidrawImageElement>
opts.fileId
string | null
ID of the file in BinaryFiles
opts.status
'pending' | 'saved' | 'error'
Loading status of the image
opts.scale
[number, number]
Scale factors [x, y] for the image

newFrameElement

Creates a frame element for grouping.
function newFrameElement(
  opts: {
    name?: string;
  } & ElementConstructorOpts
): NonDeleted<ExcalidrawFrameElement>
opts.name
string | null
Optional name for the frame

Element Mutation

mutateElement

Mutates an existing element with updates and bumps its version.
function mutateElement<TElement extends ExcalidrawElement>(
  element: TElement,
  elementsMap: ElementsMap,
  updates: ElementUpdate<TElement>,
  options?: {
    isDragging?: boolean;
  }
): TElement
element
ExcalidrawElement
required
The element to mutate
elementsMap
ElementsMap
required
Map of all elements (for context)
updates
Partial<TElement>
required
Properties to update (excludes ‘id’ and ‘updated’)
options.isDragging
boolean
Whether the element is being dragged
element
TElement
The mutated element with updated version and versionNonce
Example:
import { mutateElement } from "@excalidraw/excalidraw";

mutateElement(element, elementsMap, {
  x: 150,
  y: 200,
  width: 300,
});

newElementWith

Creates a new element instance with updates (immutable operation).
function newElementWith<TElement extends ExcalidrawElement>(
  element: TElement,
  updates: ElementUpdate<TElement>,
  force?: boolean
): TElement
element
ExcalidrawElement
required
The base element
updates
Partial<TElement>
required
Properties to update
force
boolean
default:"false"
Force regeneration even if no changes detected
element
TElement
New element instance with updates applied
Example:
import { newElementWith } from "@excalidraw/excalidraw";

const updatedElement = newElementWith(element, {
  backgroundColor: "#ff0000",
  opacity: 80,
});

bumpVersion

Bumps element version, versionNonce, and updated timestamp.
function bumpVersion<T extends ExcalidrawElement>(
  element: T,
  version?: number
): T
element
ExcalidrawElement
required
The element to bump
version
number
Optional specific version to set (will be incremented by 1)
Example:
import { bumpVersion } from "@excalidraw/excalidraw";

bumpVersion(element);

Element Queries

getNonDeletedElements

Filters out deleted elements from an array.
function getNonDeletedElements<T extends ExcalidrawElement>(
  elements: readonly T[]
): readonly NonDeleted<T>[]
elements
readonly ExcalidrawElement[]
required
Array of elements to filter
elements
NonDeleted<ExcalidrawElement>[]
Array of non-deleted elements
Example:
import { getNonDeletedElements } from "@excalidraw/excalidraw";

const activeElements = getNonDeletedElements(allElements);

getSceneVersion

Calculates the scene version by summing element versions.
function getSceneVersion(elements: readonly ExcalidrawElement[]): number
elements
readonly ExcalidrawElement[]
required
Elements to calculate version from
version
number
Sum of all element versions
Note: This function is deprecated. Use hashElementsVersion instead for better performance.

hashElementsVersion

Generates a hash of elements’ versionNonce values using the djb2 algorithm.
function hashElementsVersion(elements: ElementsMapOrArray): number
elements
ElementsMapOrArray
required
Elements to hash (array or Map)
hash
number
Unsigned 32-bit integer hash
Example:
import { hashElementsVersion } from "@excalidraw/excalidraw";

const versionHash = hashElementsVersion(elements);

hashString

Hashes a string using the djb2 algorithm.
function hashString(s: string): number
s
string
required
String to hash
hash
number
Unsigned 32-bit integer hash

Element Bounds

getElementAbsoluteCoords

Gets absolute coordinates of an element in scene coordinates.
function getElementAbsoluteCoords(
  element: ExcalidrawElement,
  elementsMap: ElementsMap,
  includeBoundText?: boolean
): [number, number, number, number, number, number]
element
ExcalidrawElement
required
The element to get coordinates for
elementsMap
ElementsMap
required
Map of all elements
includeBoundText
boolean
default:"false"
Whether to include bound text in calculations
coords
[x1, y1, x2, y2, cx, cy]
Array containing: [x1, y1, x2, y2, centerX, centerY]
Example:
import { getElementAbsoluteCoords } from "@excalidraw/excalidraw";

const [x1, y1, x2, y2, cx, cy] = getElementAbsoluteCoords(
  element,
  elementsMap
);
console.log(`Bounds: (${x1}, ${y1}) to (${x2}, ${y2})`);
console.log(`Center: (${cx}, ${cy})`);

getElementBounds

Gets the axis-aligned bounding box for an element.
function getElementBounds(
  element: ExcalidrawElement,
  elementsMap: ElementsMap,
  nonRotated?: boolean
): Bounds
element
ExcalidrawElement
required
The element to get bounds for
elementsMap
ElementsMap
required
Map of all elements
nonRotated
boolean
default:"false"
Whether to get bounds without rotation
bounds
[minX, minY, maxX, maxY]
Bounding box coordinates

getCommonBounds

Gets the common bounding box for multiple elements.
function getCommonBounds(
  elements: ElementsMapOrArray,
  elementsMap?: ElementsMap
): Bounds
elements
ElementsMapOrArray
required
Elements to get common bounds for
elementsMap
ElementsMap
Optional elements map for context
bounds
[minX, minY, maxX, maxY]
Common bounding box containing all elements
Example:
import { getCommonBounds } from "@excalidraw/excalidraw";

const [minX, minY, maxX, maxY] = getCommonBounds(selectedElements);
const width = maxX - minX;
const height = maxY - minY;

getVisibleSceneBounds

Gets the visible bounds of the canvas viewport in scene coordinates.
function getVisibleSceneBounds(state: {
  scrollX: number;
  scrollY: number;
  width: number;
  height: number;
  zoom: { value: number };
}): SceneBounds
state.scrollX
number
required
Horizontal scroll offset
state.scrollY
number
required
Vertical scroll offset
state.width
number
required
Canvas width
state.height
number
required
Canvas height
state.zoom
{ value: number }
required
Current zoom value
bounds
[sceneX, sceneY, sceneX2, sceneY2]
Visible scene bounds

Element Text

refreshTextDimensions

Recalculates text element dimensions based on content and container.
function refreshTextDimensions(
  textElement: ExcalidrawTextElement,
  container: ExcalidrawTextContainer | null,
  elementsMap: ElementsMap,
  text?: string
): { text: string; x: number; y: number; width: number; height: number } | undefined
textElement
ExcalidrawTextElement
required
The text element to refresh
container
ExcalidrawTextContainer | null
required
Container element (if text is bound)
elementsMap
ElementsMap
required
Map of all elements
text
string
Optional text override
dimensions
object
Updated text, position, and dimensions

Bounding Box Utilities

elementsOverlappingBBox

Finds elements that overlap with, contain, or are inside a bounding box.
function elementsOverlappingBBox(opts: {
  elements: readonly NonDeletedExcalidrawElement[];
  bounds: Bounds | ExcalidrawElement;
  errorMargin?: number;
  type: "overlap" | "contain" | "inside";
}): readonly NonDeletedExcalidrawElement[];
elements
readonly NonDeletedExcalidrawElement[]
required
Elements to check against the bounding box.
bounds
Bounds | ExcalidrawElement
required
Bounding box as [x1, y1, x2, y2] or an element to use its bounds.
errorMargin
number
default:"0"
Safety offset in pixels to expand the bounding box.
type
'overlap' | 'contain' | 'inside'
required
  • overlap: Elements overlapping or inside bounds
  • contain: Elements inside bounds or bounds inside elements
  • inside: Elements inside bounds only
elements
NonDeletedExcalidrawElement[]
Array of elements matching the criteria.
Example:
import { elementsOverlappingBBox } from "@excalidraw/excalidraw";

// Find elements overlapping a selection box
const selectedElements = elementsOverlappingBBox({
  elements: allElements,
  bounds: [100, 100, 400, 400],
  type: "overlap",
});

// Find elements completely inside a frame
const elementsInFrame = elementsOverlappingBBox({
  elements: allElements,
  bounds: frameElement,
  type: "inside",
});

isElementInsideBBox

Checks if an element is inside a bounding box.
function isElementInsideBBox(
  element: NonDeletedExcalidrawElement,
  bbox: Bounds,
  eitherDirection?: boolean
): boolean;
element
NonDeletedExcalidrawElement
required
Element to check.
bbox
Bounds
required
Bounding box as [x1, y1, x2, y2].
eitherDirection
boolean
default:"false"
If true, also returns true if bbox is inside element.
result
boolean
true if element is inside the bounding box.
Example:
import { isElementInsideBBox } from "@excalidraw/excalidraw";

const bounds = [0, 0, 500, 500];
const isInside = isElementInsideBBox(element, bounds);

if (isInside) {
  console.log("Element is inside the bounds");
}

elementPartiallyOverlapsWithOrContainsBBox

Checks if an element partially overlaps with or contains a bounding box.
function elementPartiallyOverlapsWithOrContainsBBox(
  element: NonDeletedExcalidrawElement,
  bbox: Bounds
): boolean;
element
NonDeletedExcalidrawElement
required
Element to check.
bbox
Bounds
required
Bounding box as [x1, y1, x2, y2].
result
boolean
true if element overlaps or contains the bounding box.
Example:
import { elementPartiallyOverlapsWithOrContainsBBox } from "@excalidraw/excalidraw";

const dragBox = [100, 100, 200, 200];
const overlaps = elementPartiallyOverlapsWithOrContainsBBox(element, dragBox);

if (overlaps) {
  console.log("Element should be selected");
}

Data Utilities

getDataURL

Converts a Blob or File to a Data URL (async).
function getDataURL(file: Blob | File): Promise<DataURL>;
file
Blob | File
required
File or Blob to convert.
dataURL
DataURL
Base64-encoded data URL string.
Example:
import { getDataURL } from "@excalidraw/excalidraw";

const file = new File(["Hello"], "test.txt", { type: "text/plain" });
const dataURL = await getDataURL(file);
console.log(dataURL); // "data:text/plain;base64,SGVsbG8="

Library Utilities

parseLibraryTokensFromUrl

Extracts library installation URL and ID token from the current page URL.
function parseLibraryTokensFromUrl(): {
  libraryUrl: string;
  idToken: string | null;
} | null;
result
object | null
Object with libraryUrl and idToken, or null if not found.
Example:
import { parseLibraryTokensFromUrl } from "@excalidraw/excalidraw";

// URL: https://excalidraw.com/#addLibrary=https://example.com/lib.excalidrawlib
const tokens = parseLibraryTokensFromUrl();

if (tokens) {
  console.log("Library URL:", tokens.libraryUrl);
  console.log("ID Token:", tokens.idToken);
}

useHandleLibrary

Hook for handling library loading, updates, and persistence.
function useHandleLibrary(opts: {
  excalidrawAPI: ExcalidrawImperativeAPI | null;
  validateLibraryUrl?: (url: string) => boolean;
} & (
  | { getInitialLibraryItems?: () => MaybePromise<LibraryItemsSource> }
  | {
      adapter: LibraryPersistenceAdapter;
      migrationAdapter?: LibraryMigrationAdapter;
    }
)): void;
excalidrawAPI
ExcalidrawImperativeAPI | null
required
Excalidraw API instance.
validateLibraryUrl
function
Custom validator for library installation URLs.
validateLibraryUrl?: (url: string) => boolean;
adapter
LibraryPersistenceAdapter
Adapter for persisting library to storage.
interface LibraryPersistenceAdapter {
  load(metadata: { source: "load" | "save" }): Promise<{
    libraryItems: LibraryItems;
  } | null>;
  save(data: { libraryItems: LibraryItems }): Promise<void>;
}
migrationAdapter
LibraryMigrationAdapter
Optional adapter for migrating from legacy storage.
interface LibraryMigrationAdapter {
  load(): Promise<{ libraryItems: LibraryItems } | null>;
  clear(): Promise<void>;
}
Example:
import { useHandleLibrary } from "@excalidraw/excalidraw";

function MyEditor() {
  const [excalidrawAPI, setExcalidrawAPI] = useState(null);

  useHandleLibrary({
    excalidrawAPI,
    adapter: {
      async load() {
        const data = localStorage.getItem("excalidraw-library");
        return data ? JSON.parse(data) : null;
      },
      async save({ libraryItems }) {
        localStorage.setItem(
          "excalidraw-library",
          JSON.stringify({ libraryItems })
        );
      },
    },
  });

  return <Excalidraw excalidrawAPI={(api) => setExcalidrawAPI(api)} />;
}

Text Utilities

setCustomTextMetricsProvider

Sets a custom text metrics provider for measuring text dimensions.
function setCustomTextMetricsProvider(
  provider: (
    text: string,
    font: string
  ) => {
    width: number;
    height: number;
  }
): void;
provider
function
required
Function that measures text and returns width/height.
provider: (text: string, font: string) => {
  width: number;
  height: number;
};
Example:
import { setCustomTextMetricsProvider } from "@excalidraw/excalidraw";

// Use a custom text measurement implementation
setCustomTextMetricsProvider((text, font) => {
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  ctx.font = font;
  
  const metrics = ctx.measureText(text);
  
  return {
    width: metrics.width,
    height: metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent,
  };
});

Types

CaptureUpdateAction

Enum-like object that controls when element updates are captured in the undo/redo history.
const CaptureUpdateAction = {
  IMMEDIATELY: "IMMEDIATELY",
  NEVER: "NEVER",
  EVENTUALLY: "EVENTUALLY",
};

type CaptureUpdateActionType = ValueOf<typeof CaptureUpdateAction>;
Values:
  • IMMEDIATELY - Updates are immediately undoable. Use for most local updates.
  • NEVER - Updates never make it to undo/redo stack. Use for remote updates or scene initialization.
  • EVENTUALLY - Updates will eventually be captured as part of a future increment.
Example:
import { CaptureUpdateAction } from "@excalidraw/excalidraw";

// When updating scene programmatically
excalidrawAPI.updateScene({
  elements: newElements,
  captureUpdate: CaptureUpdateAction.NEVER, // Don't add to undo stack
});

// When updating in response to user action
excalidrawAPI.updateScene({
  elements: modifiedElements,
  captureUpdate: CaptureUpdateAction.IMMEDIATELY, // Allow undo
});

See Also