My App
BoardKitCore

Elements

The Element discriminated union — 7 element types for the whiteboard canvas

All canvas content is represented as elements. The Element type is a discriminated union over the type field, with 7 variants.

Common Types

type ElementType = 'stroke' | 'shape' | 'text' | 'image' | 'stickyNote' | 'group' | 'connector';

interface Point {
    x: number;
    y: number;
}

interface Rect {
    x: number;
    y: number;
    width: number;
    height: number;
}

type AnchorPosition = 'top' | 'right' | 'bottom' | 'left' | 'center';

ElementBase

All element types share this base interface:

interface ElementBase {
    id: string;
    pageId: string;
    type: ElementType;
    zIndex: number;
    lockedBy?: string;
    createdBy: string;
    createdAt: string;
    updatedAt: string;
}
FieldDescription
idUnique element identifier
pageIdID of the page this element belongs to
typeDiscriminator for the element variant
zIndexRendering order (higher = on top)
lockedByUser ID that has locked this element (optional)
createdByUser ID that created this element
createdAt / updatedAtISO 8601 timestamps (used for LWW merge)

StrokeElement

A freehand drawing stroke created by the Pen tool.

interface StrokeElement extends ElementBase {
    type: 'stroke';
    data: {
        points: Point[];
        pressures?: number[];
        style: StrokeStyle;
        bounds: Rect;
    };
}

ShapeElement

A geometric shape (rectangle, ellipse, line, arrow, triangle).

interface ShapeElement extends ElementBase {
    type: 'shape';
    data: {
        shapeType: 'rectangle' | 'ellipse' | 'line' | 'arrow' | 'triangle';
        position: Point;
        size: { width: number; height: number };
        rotation: number;
        style: ShapeStyle;
        bounds: Rect;
    };
}

TextElement

A text box with rich text styling.

interface TextElement extends ElementBase {
    type: 'text';
    data: {
        content: string;
        position: Point;
        size: { width: number; height: number };
        rotation: number;
        style: TextStyle;
        bounds: Rect;
    };
}

ImageElement

An uploaded image placed on the canvas.

interface ImageElement extends ElementBase {
    type: 'image';
    data: {
        assetId: string;
        url: string;
        position: Point;
        size: { width: number; height: number };
        rotation: number;
        bounds: Rect;
    };
}

StickyNoteElement

A colored sticky note with text.

interface StickyNoteElement extends ElementBase {
    type: 'stickyNote';
    data: {
        content: string;
        position: Point;
        size: { width: number; height: number };
        color: string;
        style: TextStyle;
        bounds: Rect;
    };
}

GroupElement

A virtual container grouping child elements.

interface GroupElement extends ElementBase {
    type: 'group';
    data: {
        childIds: string[];
        bounds: Rect;
    };
}

ConnectorElement

A line connecting two elements with optional arrows.

interface ConnectorElement extends ElementBase {
    type: 'connector';
    data: {
        startElementId: string;
        endElementId: string;
        startAnchor: AnchorPosition;
        endAnchor: AnchorPosition;
        waypoints: Point[];
        style: StrokeStyle;
        arrowStart: boolean;
        arrowEnd: boolean;
        bounds: Rect;
    };
}

The Element Union

type Element =
    | StrokeElement
    | ShapeElement
    | TextElement
    | ImageElement
    | StickyNoteElement
    | GroupElement
    | ConnectorElement;

Use the type field to discriminate:

function getElementLabel(element: Element): string {
    switch (element.type) {
        case 'stroke': return 'Drawing';
        case 'shape': return element.data.shapeType;
        case 'text': return element.data.content.slice(0, 20);
        case 'image': return 'Image';
        case 'stickyNote': return 'Sticky Note';
        case 'group': return `Group (${element.data.childIds.length})`;
        case 'connector': return 'Connector';
    }
}

On this page