A JAVASCRIPT LIBRARY
Rich text,
drawn on canvas.
Rich text for canvas editors, charts, and image exports. Use HTML and CSS for styles; draw with native Canvas 2D commands.
npm i render-tagRich text.
Canvas 2D.
NATIVE CANVAS 2D
Uses standard Canvas 2D drawing commands.
SYNCHRONOUS
Returns in the same call. Load fonts first.
ZERO DEPENDENCIES
No third-party runtime packages.
LIVE EXAMPLE
HTML → canvas.
Write familiar tags and styles.
render-tag lays out the text and draws it to a canvas.
import { render } from 'render-tag';
const { canvas } = render({
html: '<p>Hello <strong>world</strong></p>',
width: 400,
});These previews run the code shown above. The page loads the fonts before drawing.
LAYOUT WITHOUT PAINTING
Text measurements.
Get content height, line boxes, and positioned runs. Use them to fit text into a space, draw backgrounds, or build hit areas.
Layout & headless examples →Blue shapes come from the measured line boxes.
LIVE EDITOR
Interactive rich text.
Edit text and formatting.
Change the width to see reflow.
Make something worth reading.
A little bold. A little italic. A whole lot of possibility.
EXAMPLES
Rendering examples.
Gradients, outlines, decorations and curves.
Every example below is a real canvas. Open its source.
Gradient fills
02Outlined type
03Decorations
04Mixed fonts
05Shadows & glow
06Text on a path
07Nested lists
08RTL & mixed directions
09Alignment & justification
10Spacing & transforms
11Typographic hierarchy
12PERFORMANCE
Render time comparison.
Compare fresh HTML → canvas rendering of the same rich text, on your device.
No cached render results or layouts. Each sample renders from fresh HTML. Fonts are preloaded; browser resource caches may still apply.
| Library | Time · lower is faster | Fresh render · median | vs render-tag | Status |
|---|---|---|---|---|
| render-tag this build | — | — | Not run | |
| snapdom 3.3.0 | — | — | Not run | |
| modern-screenshot 4.7.0 | — | — | Not run | |
| html2canvas 1.4.1 | — | — | Not run | |
| dom-to-image-more 3.11.0 | — | — | Not run |
No prerecorded scores. Run to load the comparison libraries and measure in this browser.
Method & rendered outputs
Rich text mixing Roboto, Playfair Display, Merriweather, and Lobster (self-hosted Google Fonts). 400px width, 1× resolution, two warm-ups, median of seven fresh renders per library. Each timed DOM capture uses a new root element and fresh content parsed from HTML; render-tag parses that same HTML internally. SnapDOM uses invalidate: true and cache: false to disable capture memoization and persistent library caches. Modern-screenshot and html2canvas use new contexts; dom-to-image-more resets per-render resource state and disables cached default-style copying. Font and module loading are excluded. Browser font, glyph, and HTTP caches are not disabled; work may be cached within a single call. Each render includes a pixel readback, and any font embedding inside the call is timed. Libraries have different goals: rich text drawing versus DOM capture. Compare output quality as well as speed.
USAGE
Core API.
Render directly, use measurements in your design,
or measure text in a headless workflow.
import { render } from 'render-tag';
// Load the fonts your content uses before rendering.
await Promise.all([
document.fonts.load('400 24px "Roboto"'),
document.fonts.load('700 24px "Roboto"'),
]);
const { canvas, height } = render({
html: '<p style="font-family:Roboto;font-size:24px">Hello <b>world</b></p>',
width: 400,
});
document.body.appendChild(canvas);
import { layout, drawLayout } from 'render-tag';
const result = layout({
html: '<p>Hello <b>world</b></p>',
width: 280,
});
// Use measurements to check whether text fits your design.
const fits = result.height <= 200;
// Boxes expose lineBoxes; text runs carry positions and styles.
console.log(result.layoutRoot);
// Paint the measured text when you need a preview.
const { canvas } = drawLayout({ layout: result, width: 280 });
import { layout, setDOMParser } from 'render-tag';
import { DOMParser } from 'linkedom';
import { createCanvas } from '@napi-rs/canvas';
// Your application supplies these two adapters.
setDOMParser(new DOMParser());
const ctx = createCanvas(1, 1).getContext('2d');
// Register your fonts before measuring. No drawing or DOM layout.
const result = layout({ html: '<p>Measure this text.</p>', width: 400, ctx });
console.log(result.height);
console.log(result.layoutRoot);
Load fonts first. These functions are synchronous. Recompute layout when content, styles, width or fonts change.
render() measures and draws.
layout() returns geometry without painting.
drawLayout() paints an existing layout.