Measures a rendered DOM node and keeps the graph element's size in sync with
it. Point nodeRef at the HTML or SVG node that defines the element's size;
whenever that node resizes, the matching graph element is resized to match
(optionally adjusted by a transform, see MeasureElementOptions), and
the element's current width/height are returned for your own layout math.
Reach for it when an element's size is driven by its rendered content rather than fixed up front, e.g. text that wraps or a list that grows.
Ref to the HTML or SVG node to measure. It must be mounted in the DOM while the hook runs.
Optional MeasureElementOptions; the main option is a
transform that adjusts the measured size before it is applied.
Options for useMeasureElement, controlling how the measured DOM size is turned into the graph element's size.
Optional Readonlytransform?: TransformElementLayoutAdjusts the measured size before it is written to the graph element, e.g. to
add padding or reserve space for a header. Receives the measured dimensions
plus the element's current layout (TransformElementLayoutParams) and
returns the width/height (and optionally x/y) to apply.
The graph element's current width and height (always defined).
renderElement callback (or a component rendered from
one); it reads the current cell from context and throws otherwise.useMeasureElement calls target the same element, the most
recently mounted one wins. When it unmounts, the previous one takes over
again.useCell((cell) => cell.size)) in the same component. This hook already
syncs the size and returns the live width/height; reading it again only
adds a redundant subscription and an extra render.If used outside a renderElement context, or if the current cell is a
link rather than an element.
Basic usage
import { useMeasureElement } from '@joint/react';
import { useRef } from 'react';
// The element grows to fit its text label.
function LabelElement() {
const textRef = useRef<SVGTextElement>(null);
const { width, height } = useMeasureElement(textRef);
return (
<>
<rect width={width} height={height} fill="#333" />
<text ref={textRef} x={4} y={16} fill="#fff">Hello world</text>
</>
);
}
Use the returned size
import { useMeasureElement } from '@joint/react';
import { useRef } from 'react';
const iconURL = 'https://example.com/icon.svg';
// Size follows the HTML content; use the returned size to place an icon inside.
function Card() {
const contentRef = useRef<HTMLDivElement>(null);
const { width, height } = useMeasureElement(contentRef);
const iconSize = 16;
return (
<>
<rect width={width} height={height} fill="#333" />
<image href={iconURL} x={width - iconSize} y={0} width={iconSize} height={iconSize} />
<foreignObject width={width} height={height}>
<div ref={contentRef} style={{ padding: 8, color: '#fff' }}>Card content</div>
</foreignObject>
</>
);
}
Adjust size with a transform
import { useMeasureElement, type TransformElementLayout } from '@joint/react';
import { useRef, useCallback } from 'react';
function ListElement() {
const divRef = useRef<HTMLDivElement>(null);
const padding = 10;
const headerHeight = 50;
const transform: TransformElementLayout = useCallback(
({ width: measuredWidth, height: measuredHeight }) => {
return {
width: padding + measuredWidth + padding,
height: headerHeight + measuredHeight + padding,
};
},
[]
);
const { width, height } = useMeasureElement(divRef, { transform });
return (
<>
<rect width={width} height={height} fill="#121826" />
<foreignObject x={padding} y={headerHeight} width={width - 2 * padding} height={height - headerHeight - padding}>
<div ref={divRef}>Content</div>
</foreignObject>
</>
);
}
useMeasureElement()