HTML-rendering techniques — reference
Diataxis type: reference. Neutral facts for selecting a technique while working. For
the reasoning behind these choices see the explanation; for a recipe
see the how-to. Current as of 2026-05.
The seven techniques
| # |
Technique |
Category |
Renderer |
Maturity (2026) |
Accessible / crawlable output |
Licence |
| 1 |
Native HTML-in-Canvas (drawElementImage/placeElement) |
UI → canvas |
Real browser |
Chrome 148–151 origin trial; Chromium-only; Gecko/WebKit no signal |
Yes (only canvas technique that is) |
— |
| 2 |
DOM→raster libs (html2canvas, html-to-image, snapdom) |
HTML → image |
Library's own painter (not the browser) |
Mixed; html2canvas unmaintained at 1.4.1; snapdom/html-to-image active |
N/A (flat raster) |
MIT |
| 3 |
Satori (+ resvg) / @vercel/og |
data → image |
Satori flexbox engine (CSS subset) |
Stable, active |
N/A |
MIT |
| 4 |
SVG <foreignObject> → drawImage |
HTML → image |
Real browser (taints canvas) |
Stable |
N/A |
— |
| 5 |
Headless browser (Puppeteer / Playwright) |
HTML → image/PDF |
Real Chromium |
Stable |
N/A |
Apache-2.0 |
| 6 |
Canvas-native UI (Konva / Fabric / PixiJS) |
UI in canvas |
GPU/2D canvas (no DOM) |
Stable |
No (no DOM, no a11y by default) |
PixiJS/Fabric MIT; Konva GPL-3.0 — verify |
| 7 |
Perf primitives (OffscreenCanvas, WebGPU) |
acceleration |
GPU + workers |
WebGPU cross-browser since ~Nov 2025 |
— |
— |
Decision matrix
| Goal |
Use |
Notes |
| Dynamic OG / social images |
Satori / @vercel/og (#3) |
flexbox-only; no grid/RTL; fonts must be supplied |
| Export the current view as PNG (client-side) |
snapdom (#2) |
fails on backdrop-filter/glass; CORS/tainting applies |
| Pixel-perfect PDF receipts / contracts |
Headless Chromium (#5) |
only path using the complete engine; heavy server dep |
| Pixel-perfect screenshot of a complex page |
Playwright / Puppeteer (#5) |
full CSS (grid, blur, web fonts) |
| Interactive design-tool / whiteboard canvas |
Fabric / Konva / PixiJS (#6) |
not accessible/crawlable by default; check Konva licence |
| Generative-UI on a public site |
DOM-first (current approach); HTML-in-Canvas (#1) only as a future prototype |
canvas-native UI tanks crawlability |
| Data-viz charts |
SVG/DOM (Recharts/D3); canvas (#6/#7) only for huge datasets |
SVG charts are accessible + inspectable |
Capability facts
- CSS support is a subset:
display: flex and none only — no display: grid,
no z-index (paint order = source order), no 3D transforms, no RTL.
- Every element with more than one child must set
display: flex explicitly, or
rendering throws.
- Fonts are not taken from the system; pass
fonts: [{ name, data, weight, style }]
with TTF/OTF/WOFF buffers (not WOFF2).
- Images: supply a reachable URL or a base64 data URI; for reliability pre-fetch and
inline as a data URI.
- Output is SVG; rasterise with
@resvg/resvg-js (native binding) on Node, or
@vercel/og (satori + resvg-WASM, ~500KB bundle cap) on edge.
- Measured on this project (2026-05): warm render ≈ 320 ms (satori ≈ 18 ms + resvg ≈
303 ms) for a 1200×630 card; resvg dominates.
DOM→raster libs (#2)
- html2canvas re-implements CSS painting — it is not a real screenshot; it fails
on
box-shadow, gradients and backdrop-filter/blur.
- html2canvas is effectively unmaintained (1.4.1). Active alternatives:
html2canvas-pro, html-to-image, snapdom (≈30–100× faster).
- Cross-origin images without CORS headers taint the canvas;
toDataURL then
throws a SecurityError. Any <foreignObject>-with-HTML SVG also taints.
Native HTML-in-Canvas (#1)
- API:
layoutsubtree attribute opt-in; drawElementImage() (2D), placeElement()
(interactive 2D), texElementImage2D (WebGL), copyElementImageToTexture (WebGPU);
redraw on the paint event.
- Cross-origin content is not drawn (privacy-preserving).
- Behind
chrome://flags/#canvas-draw-element; Chromium-only; not a public-path option
in 2026.
Corrections to common assumptions
- Canvas UI is not accessible or crawlable by default.
- html2canvas is not a real screenshot and is not actively maintained.
- Satori is not a full CSS engine — it is flexbox-only.
- A tainted canvas cannot be exported with
toDataURL/toBlob.
- The native HTML-in-Canvas API is not shippable in 2026 (Chromium OT only).
- WebGPU is cross-browser as of ~Nov 2025 (no longer experimental).
- Konva may be GPL-3.0 — verify before commercial/closed-source use; PixiJS,
Fabric, two.js, Satori, Puppeteer, Playwright are permissive (MIT/Apache).