**One layout engine for JSX, HTML, and CSS. Render images, SVG, PDFs, or a paint tree. No headless browser.**
[](https://www.npmjs.com/package/takumi-js)
[](https://www.npmjs.com/package/takumi-pdf)
[](https://www.npmjs.com/package/takumi-paint)
[](https://crates.io/crates/takumi)
[](https://www.npmjs.com/package/@takumi-rs/core)
[](#license)
[Documentation](https://takumi.kane.tw/docs/) · [Playground](https://takumi.kane.tw/playground) · [Showcase](https://takumi.kane.tw/showcase)
Takumi is a layout engine written in Rust, with text and inline layout ported from Chromium. It lays out JSX, HTML, and CSS without launching a browser. One layout feeds several outputs, so the same component can become an Open Graph image, an animated GIF, an invoice PDF, or the paint tree behind editable slides.
| Use | Package | Output |
|---|---|---|
| Images and animations | takumi-js |
PNG, JPEG, WebP, ICO, raw RGBA, SVG. Animated WebP, APNG, and GIF. |
| PDF documents | takumi-pdf |
Paged or single-page PDF with selectable text. |
| Your own renderer or layout data | takumi-paint |
A paint tree of every box, text run, and image, positioned with CSS values resolved. |
| Embedding in Rust | takumi |
Images, animations, and SVG from a node tree or HTML. |
Install the package for your output. The examples below use Bun to run JSX directly.
bun i takumi-js # PNG, JPEG, WebP, SVG, animations
bun i takumi-pdf # paged PDF
bun i takumi-paint # paint tree
Save this as image.tsx and run bun image.tsx to write a 1200 × 630 PNG.
import { render } from "takumi-js";
import { writeFile } from "node:fs/promises";
const image = await render(
<div tw="w-full h-full flex items-center justify-center bg-linear-to-b from-blue-100 to-red-50">
<h1 tw="text-6xl font-bold">Hello from Takumi</h1>
</div>,
{ width: 1200, height: 630 },
);
await writeFile("./output.png", image);
Save this as document.tsx and run bun document.tsx to write an A4 PDF with a page-number footer.
import { render } from "takumi-pdf";
import { PageNumber, TotalPages } from "takumi-pdf/primitives";
import { writeFile } from "node:fs/promises";
const pdf = await render(
<main>
<h1>Invoice</h1>
<p>Total: $1,250.00</p>
</main>,
{
size: "a4",
footer: (
<div tw="flex w-full justify-center text-[10px] text-gray-500">
Page <PageNumber /> of <TotalPages />
</div>
),
},
);
await writeFile("invoice.pdf", pdf);
Save this as paint.tsx and run bun paint.tsx to print each text run with its font.
import { paint } from "takumi-paint";
const tree = await paint(
<div style={{ width: 240, padding: 16, background: "#F7F3EC" }}>
Hello <b>world</b>
</div>,
{ width: 320 },
);
for (const node of tree.nodes) {
if (node.type !== "text") {
continue;
}
for (const run of node.runs) {
console.log(run.text, run.font.family, run.fontSize);
}
}
tree.steps lists the draws in paint order. See Drawing to replay them on a canvas.
text-fit grows or shrinks a headline to fill its line box, no measuring loop.text-wrap: balance balances lines in a headline. pretty reduces short last lines.@keyframes and animate-spin become WebP, APNG, GIF, or video frames.var(), calc(), and media queries.lang picks each language's own Han glyphs for the same code points.offset-path. background-clip: text and conic gradients paint it.filter: url(...), feTurbulence and feDisplacementMap included.corner-shape swaps round corners for squircle, bevel, scoop, notch, or superellipse(n).break-before: page, break-after: page, and break-inside: avoid.<PageNumber /> and <TotalPages /> count in CSS counter styles, trad-chinese-informal included.<thead> and <tfoot> on every page.outline: true builds bookmarks from headings.takumi-pdf and takumi-paint run on WebAssembly everywhere.takumi crate.| You are using | What changes |
|---|---|
satori |
Replace satori() with renderSvg(), or call render() for encoded image bytes. Compare the renderers. |
next/og |
Swap the ImageResponse import. Keep explicit Flexbox styles and compare the rendered output. Read the migration guide. |
| Puppeteer or Playwright for PDFs | Replace page.pdf() with render(). You must preload remote assets, and Takumi supports less CSS than Chrome. Read the migration guide. |
@react-pdf/renderer |
Replace Document, View, and Text with HTML elements and CSS. Browser viewers and some text controls have no equivalent. Read the migration guide. |
pdfkit |
Use JSX or HTML instead of positioning each line. Keep pdfkit when you need low-level drawing control. |
import { render } from "takumi-js";
import { googleFonts } from "takumi-js/helpers";
const image = await render(
<div
tw="w-full h-full flex items-center justify-center"
style={{
fontSize: 72,
fontFamily: "Fraunces",
fontVariationSettings: "'opsz' 72, 'wght' 700",
}}
>
Hello from Takumi
</div>,
{
width: 1200,
height: 630,
fonts: googleFonts([{ name: "Fraunces", weight: "100..900", axes: { opsz: "9..144" } }]),
},
);
render() registers only the font subsets the content uses. To preload fonts outside the request path, register them on a Renderer and pass it as renderer. See Fonts.
next/og-compatible)import { ImageResponse } from "takumi-js/response";
export function GET() {
return new ImageResponse(
<div tw="w-full h-full flex items-center justify-center bg-linear-to-b from-blue-100 to-red-50">
<h1 tw="text-6xl font-bold">Hello from Takumi</h1>
</div>,
{ width: 1200, height: 630 },
);
}
import { renderAnimation } from "takumi-js";
import { writeFile } from "node:fs/promises";
const animation = await renderAnimation({
width: 400,
height: 400,
fps: 30,
format: "webp",
scenes: [
{
durationMs: 1000,
node: (
<div tw="w-full h-full flex items-center justify-center">
<div tw="w-32 h-32 bg-blue-500 animate-spin rounded-lg" />
</div>
),
},
],
});
await writeFile("./output.webp", animation);
import { renderSvg } from "takumi-js";
import { writeFile } from "node:fs/promises";
const svg = await renderSvg(
<div tw="w-full h-full flex items-center justify-center bg-linear-to-b from-blue-100 to-red-50">
<h1 tw="text-6xl font-bold">Hello from Takumi</h1>
</div>,
{ width: 1200, height: 630 },
);
await writeFile("./output.svg", svg);
cargo add takumi
Start from the Rust example or the crate docs.
Compare Takumi and Satori for image formats, CSS support, runtime requirements, and migration examples. For PDFs, the renderer comparison records invoice benchmarks against @react-pdf/renderer and Puppeteer, with package versions and hardware.
Takumi supports a subset of browser CSS. PDF output keeps only color filter functions. It rejects blur() and drop-shadow(), and it does not draw backdrop-filter. Keep Puppeteer when your output depends on page scripts or browser layout. Test your templates before switching renderers.
More projects in the showcase. Takumi is part of the Vercel OSS Program.
Takumi converts JSX or HTML to a node tree and lays it out in takumi-core, whose block, inline, flexbox, grid, and table layout port Chromium's Blink. HarfRust shapes text, and Skrifa reads fonts.
The laid-out tree goes to one of four backends: raster (takumi-raster), SVG (takumi-svg), PDF (takumi-pdf), or the paint tree (takumi-paint). See the node reference for the input format.
| Package | Use |
|---|---|
takumi-js |
JSX, HTML, and node trees to images, SVG, and animations. |
takumi-pdf |
JSX, HTML, and node trees to PDF documents. |
takumi-paint |
JSX, HTML, and node trees to a paint tree of used values. |
@takumi-rs/core |
Native Node.js bindings for direct node-tree rendering. |
@takumi-rs/wasm |
WebAssembly bindings for edge runtimes and browsers. |
@takumi-rs/helpers |
Template conversion, fonts, images, and emoji. |
takumi |
Rust crate for node trees and HTML to images, animations, and SVG. |
| Takumi OG image (source) | Package OG card (source) |
|---|---|
![]() |
![]() |
| Prisma-style API card (source) | X-style social post (source) |
![]() |
![]() |
| Keyframe Animation (source) | shiki-image |
![]() |
See more examples for invoices, e-invoices, Next.js, Cloudflare Workers, TanStack Start, Svelte, Rust, and ffmpeg keyframe animation.
Read CONTRIBUTING.md for local setup and the fixture workflow.
We welcome bug reports, feature requests, doc improvements, and new example integrations.
MIT or Apache-2.0
Bundled third-party code is listed in the third-party notices.
| 日期 | 榜单 | 排名 | 周期新增 | 总 STAR |
|---|