Skip to main content
Version: 4.3

<FreeTransform />

Draws interactive resize and rotate handles around a single element, so users can drag the corners/edges to resize, drag the top handle to rotate, and snap to a grid. Wraps the JointJS+ ui.FreeTransform widget and renders no DOM of its own — the handles are managed imperatively on the paper.

Use it in either mode:

  • Outside renderElement: pass the target via the cell prop, usually driven by selection state. Pass null to detach the handles.
  • Inside renderElement: omit cell; the rendered element is targeted automatically from context.

Only elements get handles — a link cell is ignored with a dev warning. Constrain behavior with props like minWidth/maxWidth, preserveAspectRatio, resizeDirections, and allowRotation, or reach any native option through the FreeTransformOptions options escape hatch. See FreeTransformProps for every supported prop.

See

FreeTransform

Examples

Attach to the selected element

import { GraphProvider, Paper, FreeTransform } from '@joint/react-plus';
import { useState } from 'react';

const cells = [{ id: '1', type: 'element', position: { x: 40, y: 40 }, size: { width: 80, height: 40 } }];

function Editor() {
const [selectedId, setSelectedId] = useState<string | null>(null);
return (
<GraphProvider initialCells={cells}>
<Paper
renderElement={() => <rect width={80} height={40} fill="#3498db" />}
onElementPointerClick={({ model }) => setSelectedId(String(model.id))}
/>
{selectedId && <FreeTransform cell={selectedId} preserveAspectRatio />}
</GraphProvider>
);
}

Inside renderElement (no cell needed)

import { GraphProvider, Paper } from '@joint/react';
import { FreeTransform } from '@joint/react-plus';

const cells = [{ id: '1', type: 'element', position: { x: 40, y: 40 }, size: { width: 80, height: 40 } }];

// Targets the rendered element automatically — no `cell` prop needed.
<GraphProvider initialCells={cells}>
<Paper renderElement={() => <FreeTransform allowRotation rotateAngleGrid={15} />} />
</GraphProvider>;

Props

allowOrthogonalResize?

readonly optional allowOrthogonalResize?: boolean;

Allow resizing from the edge (orthogonal) handles, not just the corners.

Default

true

allowRotation?

readonly optional allowRotation?: boolean;

Show the rotate handle and allow rotating the element.

Default

true

cell?

readonly optional cell?: ID | Cell<Attributes<Selectors>, ModelSetOptions> | null;

Cell to attach the handles to — a dia.Cell instance or its id (only elements get handles; a link is ignored with a dev warning). Required outside renderElement; pass null to detach. Inside renderElement leave it unset so the rendered element is targeted from context — passing cell there logs a dev warning and renders nothing.


maxHeight?

readonly optional maxHeight?: SizeConstraint;

Largest allowed height: a number, or a callback returning the limit per cell/direction.

Default

Infinity

maxWidth?

readonly optional maxWidth?: SizeConstraint;

Largest allowed width: a number, or a callback returning the limit per cell/direction.

Default

Infinity

minHeight?

readonly optional minHeight?: SizeConstraint;

Smallest allowed height: a number, or a callback returning the limit per cell/direction.

Default

0

minWidth?

readonly optional minWidth?: SizeConstraint;

Smallest allowed width: a number, or a callback returning the limit per cell/direction.

Default

0

options?

readonly optional options?: Partial<Options>;

Raw ui.FreeTransform.Options passthrough for anything joint-react-plus doesn't expose as a dedicated prop (mvc internals like theme, events, attributes, tagName, etc.). See FreeTransformOptions.

Avoid overriding options the component manages itself (cell, cellView, paper).


padding?

readonly optional padding?: Padding;

Gap, in pixels, between the element's bounding box and the transform frame.

Default

3

preserveAspectRatio?

readonly optional preserveAspectRatio?: boolean;

Lock the element's aspect ratio while resizing from a corner.

Default

false

resizeDirections?

readonly optional resizeDirections?: Direction[];

Restrict the visible handles to these directions (e.g. ['bottom-right', 'bottom-left'] keeps only the two bottom corners). Omit to show all eight. Valid values: 'top-left', 'top', 'top-right', 'right', 'bottom-right', 'bottom', 'bottom-left', 'left'.


resizeGrid?

readonly optional resizeGrid?: object;

Snap resizing to this { width, height } step. Omit to fall back to the paper's gridSize (free resizing only when that is 1).

height

height: number;

width

width: number;

rotateAngleGrid?

readonly optional rotateAngleGrid?: number;

Snap rotation to multiples of this angle, in degrees.

Default

15

useBordersToResize?

readonly optional useBordersToResize?: boolean;

Use the element's borders as resize handles instead of dedicated handle markers.

Default

false

usePaperScale?

readonly optional usePaperScale?: boolean;

Scale the handles with the paper zoom so they keep a constant on-screen size.

Default

false