Takumi

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.

The rest of this page uses takumi-js.

Install

npm i takumi-js

takumi-js ships two backends and picks one at runtime. You install nothing else.

RuntimeBackend
Node.js, Bunnative (@takumi-rs/core)
Cloudflare Workers, edge runtimes, Deno, browsersWebAssembly (@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.

image.tsx
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", );
  • width and height set 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.

app/og/route.tsx
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.

Load a variable font
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