Render JSX to images.
Skip the browser.
Takumi parses CSS, lays out the tree, shapes text, and encodes pixels in a single Rust binary. Headless Chromium spends 300 MB and a cold start on an OG card. Takumi spends a function call.
Output from example/twitter-images — every image on this page was rendered by Takumi.
The code is the design file.
ImageResponse is drop-in compatible with next/og. This component is the exact source of the card next to it.
export default function DemoCard() {
return (
<div tw="flex h-full w-full flex-col justify-between bg-[#16130f] p-14 text-white">
<div tw="flex items-center justify-between">
<span tw="text-2xl text-[#a8a29a]">takumi.kane.tw</span>
<span tw="h-10 w-10 bg-[#ff4d4d]" />
</div>
<h1
tw="text-7xl font-bold leading-tight"
style={{
backgroundClip: "text",
backgroundImage: "linear-gradient(110deg, #fff 60%, #ff4d4d)",
color: "transparent",
}}
>
This card is the code beside it.
</h1>
<div tw="flex items-center justify-between text-2xl text-[#a8a29a]">
<span>Rendered without a browser</span>
<span>1200 × 630</span>
</div>
</div>
);
}export default function DemoCard() {
return (
<div tw="flex h-full w-full flex-col justify-between bg-[#16130f] p-14 text-white">
<div tw="flex items-center justify-between">
<span tw="text-2xl text-[#a8a29a]">takumi.kane.tw</span>
<span tw="h-10 w-10 bg-[#ff4d4d]" />
</div>
<h1
tw="text-7xl font-bold leading-tight"
style={{
backgroundClip: "text",
backgroundImage: "linear-gradient(110deg, #fff 60%, #ff4d4d)",
color: "transparent",
}}
>
This card is the code beside it.
</h1>
<div tw="flex items-center justify-between text-2xl text-[#a8a29a]">
<span>Rendered without a browser</span>
<span>1200 × 630</span>
</div>
</div>
);
}home-demo-card@2x.webp · 52 KB · rendered by bun example/twitter-images
One tree, sampled across time.
The renderer takes a timestamp. A PNG is your tree at t = 0. An animated WebP is the same tree sampled across t. CSS @keyframes and Tailwind animate-* utilities resolve at render time.
@keyframes morph {
from { border-radius: 12%; transform: rotate(0deg) scale(1); }
50% { border-radius: 50%; transform: rotate(90deg) scale(0.72); }
to { border-radius: 12%; transform: rotate(180deg) scale(1); }
}The CSS you actually write.
Support reaches past the usual OG-image subset. If your generator made you remove a property, put it back.
Layout
- display: grid
- float
- position: absolute
- calc()
- z-index
Selectors
- :is()
- :where()
- ::before
- ::after
Paint
- backdrop-filter
- mix-blend-mode
- conic-gradient()
- clip-path
- mask
- background-clip: text
Text
- WOFF2 fonts
- emoji
- RTL scripts
- multi-span inline
Motion
- @keyframes
- animation
- Tailwind animate-*
Runs as a native Node.js binding, a WASM build for Cloudflare Workers and browsers, and a Rust crate. Prebuilt for macOS, Linux, and Windows on x64 and ARM64.
In production.
Dcard renders post share images with it, Fumadocs generates its docs OG images, and Nuxt OG Image ships it as a built-in renderer.
Render your first image.
bun i takumi-jsbun i takumi-jsLayout by taffy · text by parley & skrifa · SVG by resvg
MIT / Apache-2.0