Skip to main content

HTML5 canvas Text Tutorial

Canvas has no text elements. The browser gives you fillText(), which draws a string at a point and forgets it — no wrapping, no alignment, no way to ask how wide it was. Konva.Text is a shape, so the text stays a node you can move, style, measure, and hit-test.

const text = new Konva.Text({
x: 20,
y: 20,
text: 'Hello Konva',
fontSize: 24,
fontFamily: 'Arial',
fill: 'black',
});

Set width and the text wraps to it. Leave width unset and the shape sizes itself to the content.

For the full list of properties and methods, see the Text API Reference.

import Konva from 'konva';

const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});

const layer = new Konva.Layer();
stage.add(layer);

// Simple text
const simpleText = new Konva.Text({
x: stage.width() / 2,
y: 15,
text: 'Simple Text',
fontSize: 30,
fontFamily: 'Calibri',
fill: 'green'
});

simpleText.offsetX(simpleText.width() / 2);

// Complex text with background
const complexText = new Konva.Text({
x: 20,
y: 60,
text: "COMPLEX TEXT\n\nAll the world's a stage, and all the men and women merely players. They have their exits and their entrances.",
fontSize: 18,
fontFamily: 'Calibri',
fill: '#555',
width: 300,
padding: 20,
align: 'center'
});

const rect = new Konva.Rect({
x: 20,
y: 60,
stroke: '#555',
strokeWidth: 5,
fill: '#ddd',
width: 300,
height: complexText.height(),
shadowColor: 'black',
shadowBlur: 10,
shadowOffsetX: 10,
shadowOffsetY: 10,
shadowOpacity: 0.2,
cornerRadius: 10
});

layer.add(rect);
layer.add(simpleText);
layer.add(complexText);

Measuring text

A Konva.Text measures itself as soon as it exists, so you can read its size before it is on a layer.

const text = new Konva.Text({ text: 'Hello Konva', fontSize: 24 });

text.width(); // full shape width, including padding
text.height(); // full shape height, all lines, including padding
text.getTextWidth(); // width of the widest line, excluding padding
text.fontSize(); // height of a single line

Use these instead of estimating. Centring by hand is the usual reason people guess:

text.offsetX(text.width() / 2);   // exact

Two things to know:

  • getTextHeight() is deprecated. It warns in the console. Use height() for the whole shape and fontSize() for one line.
  • measureSize(string) measures a string you have not drawn, using this shape's font. It is useful for sizing something before you commit to it, and it cannot handle multiline text.
const { width } = text.measureSize('Some other string');

In React and Vue, read the same values through a ref to the node rather than approximating them — or avoid measurement entirely by giving the text a width and an align, as the demo above does.

Wrapping and ellipsis

Text wraps only when it has a width.

new Konva.Text({
text: 'A long line that will not fit on one row',
width: 200, // wrapping requires this
wrap: 'word', // 'word' (default), 'char', or 'none'
ellipsis: true, // needs a height too, or there is nothing to overflow
});

ellipsis truncates with when the text does not fit the box. It only has an effect when both width and height are set, since without a height the shape grows to fit and nothing ever overflows.

import Konva from 'konva';

const stage = new Konva.Stage({ container: 'container', width: 500, height: 260 });
const layer = new Konva.Layer();
stage.add(layer);

const sample = 'Konva.Text wraps to the width you give it, and can truncate when it runs out of room.';

[
{ y: 10, label: "wrap: 'word' (default)", config: { width: 220 } },
{ y: 95, label: "wrap: 'char'", config: { width: 220, wrap: 'char' } },
{ y: 180, label: 'ellipsis with a fixed height', config: { width: 220, height: 44, ellipsis: true } },
].forEach(({ y, label, config }) => {
layer.add(new Konva.Text({ x: 250, y, text: label, fontSize: 13, fill: '#666' }));
layer.add(
new Konva.Rect({ x: 20, y, width: 220, height: config.height || 70, stroke: '#ddd' })
);
layer.add(new Konva.Text({ x: 20, y, text: sample, fontSize: 14, padding: 4, ...config }));
});

Text and web fonts

This is the most common text bug, and it is not a Konva bug.

A DOM element re-lays-out by itself when a web font finishes loading. Canvas does not. If the text was created before the font arrived, it was measured with the fallback font, and that stale measurement is what wrapping, centring, and width() are still based on. The text usually looks right and sits in the wrong place.

Wait for the font, then force a re-measure:

await document.fonts.load('16px "Roboto"');

// Re-setting any measured attribute re-runs the measurement.
text.fontFamily('Roboto');

Konva re-measures whenever one of its text-affecting attributes changes: text, fontFamily, fontSize, fontStyle, fontVariant, lineHeight, letterSpacing, align, verticalAlign, padding, width, height, wrap, ellipsis, and direction. Setting one of those to the value it already has is enough.

There is a complete example, including loading the font itself, in Custom Font.