Skip to main content
Version: 4.3

SVG Text

<SVGText> is the text building block for custom SVG elements in @joint/react. Use a plain <text> node for short one-line labels. Reach for <SVGText> when text needs wrapping, ellipsis, multiline alignment, or richer styling inside an SVG shape.

When text should drive an element's size rather than fit inside a fixed one, pair <SVGText> with Measuring Elements.

When to use itโ€‹

  • Use plain <text> when the label is short and never wraps
  • Use <SVGText> when text must stay inside a bounded SVG area
  • Combine <SVGText> with useMeasureElement() when the text should define the element size

Basic usageโ€‹

This fixed-size task card uses useCell(selectElementSize) for the current node dimensions and lets <SVGText> handle both line breaking and truncation. The three cards below have increasing text lengths: notice how the title clamps to one line and the description wraps and truncates with an ellipsis.

Wrapping and clampingโ€‹

textWrap enables line breaking. You can pass true for the default behavior or an options object when the label needs clamping or ellipsis.

<SVGText width={160} textWrap>
A longer label that should wrap across multiple lines.
</SVGText>

<SVGText
width={160}
textWrap={{ ellipsis: true, maxLineCount: 2 }}
>
A longer label that should wrap, but only across two visible lines.
</SVGText>

width defines the wrapping box. maxLineCount is enough when you only want to cap the number of visible lines. height becomes useful when the text also needs to fit a fixed vertical area inside the shape.

If you omit width while textWrap is enabled, <SVGText> can fall back to the current element width. In practice, an explicit inner width is usually clearer because most custom nodes want text padding rather than full edge-to-edge wrapping.

Positioning and alignmentโ€‹

x and y set the text anchor point. textAnchor controls horizontal alignment, and textVerticalAnchor controls vertical alignment inside the text box.

textAnchorHorizontaltextVerticalAnchorVertical
"start"Left-aligned with x"top"Top-aligned with y
"middle"Centered with x"middle"Centered with y
"end"Right-aligned with x"bottom"Bottom-aligned with y

Toggle the buttons below to see how each combination positions the text. The blue dot marks the anchor point, and the dashed lines show the alignment axes.

Combining with useMeasureElement()โ€‹

If text should define the node size instead of fitting inside a fixed one, combine <SVGText> with useMeasureElement() in Measuring Elements.

import { SVGText, useMeasureElement } from '@joint/react';
import { useRef } from 'react';

function StatusBadge({ label }: { label: string }) {
const groupRef = useRef<SVGGElement>(null);

const { width, height } = useMeasureElement(groupRef, {
transform: ({ width: measuredWidth, height: measuredHeight }) => ({
width: measuredWidth + 24,
height: measuredHeight + 16,
}),
});

return (
<>
<rect width={width} height={height} rx={999} fill="#1d4ed8" />
<g ref={groupRef} transform="translate(12, 8)">
<SVGText fill="white">{label}</SVGText>
</g>
</>
);
}

Here <SVGText> still handles the SVG text rendering, while useMeasureElement() turns that rendered content into the final element size.

Advanced text optionsโ€‹

<SVGText> also supports a few higher-level text features that are awkward to build by hand with raw SVG:

  • annotations for styling ranges of text without manually creating <tspan> nodes
  • displayEmpty when an empty text node should still stay rendered
  • textPath when the text should follow an SVG path
<SVGText
fill="#e2e8f0"
annotations={[{ start: 0, end: 5, attrs: { fill: '#22c55e', fontWeight: 700 } }]}
>
READY ยท build-184
</SVGText>

For most custom elements, wrapping and alignment are the main reasons to use <SVGText>. The advanced options are there when the label needs more than a single uniform style.

Practical notesโ€‹

  • children must be a string
  • Use <SVGText> inside renderElement or a component rendered from it
  • Start with explicit width and height values when you are building a bounded label area
  • Do not also call useCell(selectElementSize) in the same component when measuring with useMeasureElement; useMeasureElement already returns the live size