Text

Placing and styling text inside SVG

The <text> element

SVG text is positioned with absolute coordinates, not reflowed like HTML. The x and y attributes place the baseline of the first character — not the top-left corner of the bounding box. This is the most common point of confusion when switching from HTML layout to SVG.

Two attributes control how the text aligns relative to the anchor point you provide: text-anchor handles the horizontal axis and dominant-baseline handles the vertical axis.

text-anchor: horizontal alignment

text-anchor accepts three values:

  • start — the given x is the left edge of the text (the default for left-to-right scripts).
  • middle — the given x is the horizontal center of the text.
  • end — the given x is the right edge of the text.

The demo below draws three labels all anchored to the same vertical line (x = 100). Notice how the text appears to the right, centered on, or to the left of that line depending on the text-anchor value. This matters any time you want to center a label over a data point or right-align a y-axis label in a chart.

<svg viewBox="0 0 200 100" width="260" height="130" role="img" aria-label="Three text labels showing start, middle, and end text-anchor values aligned to a vertical reference line"> <!-- Reference line at x=100 --> <line x1="100" y1="5" x2="100" y2="95" stroke="#ccc" stroke-width="1" stroke-dasharray="4 3" /> <text x="100" y="28" text-anchor="start" font-family="sans-serif" font-size="14" fill="#333">start</text> <text x="100" y="53" text-anchor="middle" font-family="sans-serif" font-size="14" fill="#d6452c">middle</text> <text x="100" y="78" text-anchor="end" font-family="sans-serif" font-size="14" fill="#0f766e">end</text> </svg> start middle end

dominant-baseline: vertical alignment

By default, y places the alphabetic baseline — the line that most lowercase letters sit on. If you want to center text vertically in a shape (say, a label inside a circle), use dominant-baseline="middle" combined with text-anchor="middle". The value auto (default), middle, hanging (top of cap height), and central are the most useful.

<svg viewBox="0 0 200 80" width="260" height="104" role="img" aria-label="A label centered inside a rectangle using text-anchor middle and dominant-baseline middle"> <rect x="40" y="10" width="120" height="60" rx="6" fill="none" stroke="#1f8a4c" stroke-width="2" /> <text x="100" y="40" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="15" fill="#1f8a4c"> Centered </text> </svg> Centered

Styling text

SVG text can be styled using either presentation attributes (directly on the element) or CSS. Both approaches produce the same result, but CSS wins when there is a conflict — it has higher specificity than presentation attributes. In practice, you will see both in the wild; using CSS classes keeps your SVG cleaner and easier to theme.

The key text presentation attributes are: font-family, font-size, font-weight, font-style, fill (text color), stroke (outline), and letter-spacing.

Attributes vs CSS

<svg viewBox="0 0 240 130" width="300" height="162" role="img" aria-label="Styled text examples: bold heading, lighter body text, and outlined display text"> <style> .svg-heading { font-family: Georgia, serif; font-size: 22px; font-weight: bold; fill: #333; } .svg-body { font-family: sans-serif; font-size: 13px; fill: #555; } </style> <!-- Styled via CSS class --> <text x="12" y="34" class="svg-heading">Heading Text</text> <text x="12" y="58" class="svg-body">Body text uses a lighter weight</text> <!-- Styled via presentation attributes --> <text x="12" y="95" font-family="sans-serif" font-size="28" font-weight="900" fill="none" stroke="#d6452c" stroke-width="1.5" letter-spacing="2">OUTLINED</text> <text x="12" y="120" class="svg-body" font-style="italic" fill="#e07b00"> Presentation attribute overridden by inline font-style </text> </svg> Heading Text Body text uses a lighter weight OUTLINED Presentation attribute overridden by inline font-style

Notice that the outlined text uses fill="none" with a stroke — a technique with no HTML equivalent. Because SVG text is vector, its outlines are actual geometric paths, making it stroke-able just like any other shape.

Multi-line text with <tspan>

Unlike HTML, SVG <text> does not wrap automatically. If the text is wider than the SVG viewport, it simply overflows — there is no block formatting context. To create multi-line text you use <tspan> child elements, each on its own line.

The typical pattern is to set an absolute x on each <tspan> (to reset the left margin) and a relative dy (to advance down by one line height). Think of dy as "move down n units from the last baseline" — it is a delta, not an absolute y coordinate.

x reset + dy advance

<svg viewBox="0 0 220 110" width="280" height="140" role="img" aria-label="Three lines of multi-line text using tspan elements with x and dy attributes"> <text x="16" y="30" font-family="sans-serif" font-size="15" fill="#333"> <tspan x="16" dy="0">SVG text does not wrap.</tspan> <tspan x="16" dy="22">Use tspan with dy to move</tspan> <tspan x="16" dy="22" fill="#d6452c">to the next line manually.</tspan> </text> </svg> SVG text does not wrap. Use tspan with dy to move to the next line manually.

The first <tspan> has dy="0" — it sits exactly on the parent's y="30" baseline. Each subsequent span uses dy="22" to step down 22 user units (roughly 1.4× the 15px font size, a comfortable line height). Individual spans can override inherited attributes — here the third line overrides fill to highlight the key takeaway.

Text on a path

SVG can flow text along any arbitrary <path> using the <textPath> element. The technique requires two steps: first, define a path inside <defs> (so it is reusable but not rendered on its own), then reference it from a <textPath href="#id"> inside a <text> element.

Each character's baseline is placed tangent to the path at its corresponding position. The startOffset attribute moves the text along the path — "50%" combined with text-anchor="middle" centers the text on the curve, which is the most common use case.

Curved text with <textPath>

<svg viewBox="0 0 260 120" width="320" height="147" role="img" aria-label="The text 'Text flows along the curve' rendered along a curved arc path"> <defs> <!-- A gentle upward arc across the SVG --> <path id="text-arc" d="M 20 90 Q 130 10 240 90" /> </defs> <!-- Show the path itself so the relationship is visible --> <use href="#text-arc" fill="none" stroke="#ccc" stroke-width="1.5" stroke-dasharray="5 4" /> <text font-family="sans-serif" font-size="15" fill="#1f8a4c"> <textPath href="#text-arc" startOffset="50%" text-anchor="middle"> Text flows along the curve </textPath> </text> </svg> Text flows along the curve

Text on a circular path

A full-circle <path> (two arc commands) lets you wrap text around a circle — a common technique for badges and labels.

<svg viewBox="0 0 160 160" width="200" height="200" role="img" aria-label="The text 'Around the circle' following a circular path"> <defs> <!-- Circle path: center (80,80), radius 60. Defined as two semicircular arcs so the path has a clear start/end. --> <path id="circle-path" d="M 80 20 A 60 60 0 1 1 79.99 20" /> </defs> <circle cx="80" cy="80" r="60" fill="none" stroke="#e07b00" stroke-width="1.5" /> <text font-family="sans-serif" font-size="13" fill="#e07b00" letter-spacing="2"> <textPath href="#circle-path" startOffset="50%" text-anchor="middle"> Around the circle </textPath> </text> </svg> Around the circle

The path defined in <defs> is not rendered — it only provides the geometry for the text to follow. The visible circle is a separate <circle> element drawn on top. This separation of "guide path" and "visible shape" is a common SVG authoring pattern: define the math in <defs>, display separately.