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-tag
RICH TEXT ON CANVAS400 × 300

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.

JAVASCRIPT
import { render } from 'render-tag';

const { canvas } = render({
  html: '<p>Hello <strong>world</strong></p>',
  width: 400,
});
CANVAS OUTPUT400 × 140

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 →
LINE BOXESMeasuring…

Blue shapes come from the measured line boxes.

LIVE EDITOR

Interactive rich text.

Edit text and formatting.
Change the width to see reflow.

Render — ms
RICH TEXT INPUTEditable

Make something worth reading.

A little bold. A little italic. A whole lot of possibility.

CANVAS OUTPUTCanvas 2D
Both panels use the selected content width.

EXAMPLES

Rendering examples.

Gradients, outlines, decorations and curves.
Every example below is a real canvas. Open its source.

Rich inline styles

01

Gradient fills

02

Outlined type

03

Decorations

04

Mixed fonts

05

Shadows & glow

06

Text on a path

07

Nested lists

08

RTL & mixed directions

09

Alignment & justification

10

Spacing & transforms

11

Typographic hierarchy

12

PERFORMANCE

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.

Render time comparison. Lower is faster.
LibraryTime · lower is fasterFresh render · medianvs render-tagStatus
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.

Comparison libraries load from esm.sh only when you run this benchmark.

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);

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.