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