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.
- Vanilla
- React
- Vue
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);
import { Stage, Layer, Text, Rect } from 'react-konva';
const text = `COMPLEX TEXT
All the world's a stage, and all the men and women merely players. They have their exits and their entrances.`;
const App = () => {
return (
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
<Text
x={0}
y={15}
width={window.innerWidth}
align="center"
text="Simple Text"
fontSize={30}
fontFamily="Calibri"
fill="green"
/>
<Rect
x={20}
y={60}
stroke="#555"
strokeWidth={5}
fill="#ddd"
width={300}
height={200} // Approximate height
shadowColor="black"
shadowBlur={10}
shadowOffsetX={10}
shadowOffsetY={10}
shadowOpacity={0.2}
cornerRadius={10}
/>
<Text
x={20}
y={60}
text={text}
fontSize={18}
fontFamily="Calibri"
fill="#555"
width={300}
padding={20}
align="center"
/>
</Layer>
</Stage>
);
};
export default App;
<template>
<v-stage :config="stageSize">
<v-layer>
<v-rect :config="rectConfig" />
<v-text :config="simpleTextConfig" />
<v-text :config="complexTextConfig" />
</v-layer>
</v-stage>
</template>
<script setup>
const stageSize = {
width: window.innerWidth,
height: window.innerHeight
};
const simpleTextConfig = {
x: 0,
y: 15,
width: window.innerWidth,
align: 'center',
text: 'Simple Text',
fontSize: 30,
fontFamily: 'Calibri',
fill: 'green'
};
const complexTextConfig = {
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 rectConfig = {
x: 20,
y: 60,
stroke: '#555',
strokeWidth: 5,
fill: '#ddd',
width: 300,
height: 200, // Approximate height
shadowColor: 'black',
shadowBlur: 10,
shadowOffsetX: 10,
shadowOffsetY: 10,
shadowOpacity: 0.2,
cornerRadius: 10
};
</script>
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. Useheight()for the whole shape andfontSize()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.