Introduction
Takumi lays out JSX, HTML, and CSS with one engine and turns the result into images, PDFs, or paint trees, without a browser.
Takumi is one layout engine, with text and inline layout ported from Chromium. It turns JSX, HTML, and CSS into images, PDFs, and other outputs without a headless browser.
Pick a package
Each package runs the same layout engine. Pick the one that produces your output.
Images and animations
takumi-js: PNG, JPEG, WebP, SVG, and animated WebP, APNG, or GIF. Open Graph images are one use.
PDF documents
takumi-pdf: paged documents such as invoices, reports, and tickets.
Paint tree
takumi-paint: the shapes, text runs, and images to draw with Canvas, slides, or a native API.
Measure
measure() in takumi-js: box sizes and text runs without painting.
The rest of this page uses takumi-js.
Install
npm i takumi-jstakumi-js ships two backends and picks one at runtime. You install nothing else.
| Runtime | Backend |
|---|---|
| Node.js, Bun | native (@takumi-rs/core) |
| Cloudflare Workers, edge runtimes, Deno, browsers | WebAssembly (@takumi-rs/wasm) |
Render an image
render takes JSX and returns encoded image bytes. Save this as image.tsx and run bun image.tsx. In Node.js, compile the JSX first.
import { } from "takumi-js";
import { } from "node:fs/promises";
const = await (
<
={{
: 72,
: "linear-gradient(to bottom right, #fff7ed, #fecaca)",
}}
="w-full h-full flex items-center justify-center"
>
Hello Takumi
</>,
{ : 1200, : 630 },
);
await ("hello.png", );widthandheightset the image size. Leave one out to size that side to the content.tw="w-full h-full"makes the root fill the image.
Serve it over HTTP
ImageResponse extends the web Response. Return it from any route handler that uses Request and Response. Its API matches next/og.
import { } from "takumi-js/response";
export function () {
return new (< ="w-full h-full grid place-items-center">Hello Takumi</>, {
: 1200,
: 630,
});
}Takumi never reads system fonts. The only built-in font is Geist, which covers Latin at weights
300 to 800. Load fonts for other scripts through fonts, or their glyphs go missing. See
Fonts.
Load a font
googleFonts loads Google Fonts families in one request. A weight range or an axes entry loads the variable font. Then font-weight and font-variation-settings set its axes per element.
import { } from "takumi-js";
import { } from "takumi-js/helpers";
const = await (
<
={{
: 72,
: "Fraunces",
: "'opsz' 72, 'wght' 700",
}}
="w-full h-full flex items-center justify-center"
>
Hello Takumi
</>,
{
: 1200,
: 630,
: ([
{
: "Fraunces",
: "100..900",
: { : "9..144" },
},
]),
},
);fonts also takes a font URL, raw bytes, or a descriptor. To load fonts before the first request, register them on a Renderer.
Coming from satori
A bare <div> lays out as a block, as in a browser. Keep explicit Flexbox styles when you move a Satori template, and compare the output. renderSvg() returns SVG like satori() does. render() returns encoded image bytes. See the Satori comparison and migration guide.
Next steps
Last updated on