poster-ai
v0.5.0
Published
Single-file distributable React posters — one .tsx file, every format you'll ever need. Works as a CLI and as a library.
Maintainers
Readme
poster
One
.tsxfile, every format you'll ever need.
Write a React component. Get a self-contained .html file, a PNG, a PDF, an
SVG, a JPG, or a WebP at any canvas size. Works as a CLI for humans and
as a library for agents and services.
npm install -g poster-ai # CLI (installs the `poster` binary)
npm install poster-ai # libraryCLI
poster build app.tsx -o app.html # self-contained .html
poster export app.tsx -o out.png # PNG, browserless by default
poster export app.tsx -o out.pdf --engine chrome # pdf / svg / jpg / webpThe canvas comes from the TSX itself. Declare w-[Npx] (and optionally h-[Npx]) on the root element - the renderer measures it and crops to that exact box. --width / --height are optional overrides for forcing a viewport size. Every command also supports --json and --quiet.
Two engines. PNG export defaults to takumi — a pure-Rust headless renderer that needs no Chrome download. Pass --engine chrome for PDF, SVG, JPG, WebP, or for pixel parity with Chrome on complex CSS (gradient text, multi-layer blurs, SVG <text>). See Engines for the trade-offs.
Inline authoring for agents
Pass - as the entry and pipe TSX on stdin:
poster export - -o hero.png <<'EOF'
export default function() {
return (
<div className="w-[1200px] h-[600px] flex items-center justify-center bg-black text-white">
<h1 className="text-7xl font-black">Hello, poster.</h1>
</div>
);
}
EOFStdin is persisted to .poster/hero.tsx by default so you can iterate -
either re-pipe updated TSX, or edit the saved file and run
poster export .poster/hero.tsx -o hero.png. Pass --ephemeral for
one-shot CI renders that touch no disk.
Library
import { writeFileSync } from "node:fs";
import { Poster } from "poster-ai";
const poster = new Poster();
// TSX → self-contained HTML string
const html = await poster.buildHtml(
{ tsx: `export default () => <h1 className="text-5xl p-10">Hi</h1>` },
{ title: "Hello", width: 1200, height: 600 },
);
// TSX → PNG Buffer via the default (takumi) engine. No browser needed.
const png = await poster.render(
{ tsx: source },
{ format: "png", width: 1600, height: 900 },
);
writeFileSync("poster.png", png);
// PDF / SVG / JPG / WebP need the chrome engine.
const pdf = await new Poster({ engine: "chrome" }).render(
{ file: "./app.tsx" },
{ format: "pdf", width: 1400, height: 1800 },
);- Discriminated input.
{ tsx }for in-memory source,{ file }for a path. No ambiguity. - Pure. Returns data; the caller writes it. No stdout writes, no
process.exit, errors throw. - Typed. Full
.d.tsshipped.BuildOptions,RenderOptions,ExportFormat,DEFAULTSall exported.
Gallery
All 52 examples below render through the same pipeline. Each row pairs the rendered output with the prompt that produced it. The 14 hand-authored seed examples (brutalist, calendar, concert, dashboard, dataart, devwrap, editorial, fitness, memphis, neon, showcase, vogue, weather, wrapped) have reverse-engineered prompts; the rest were generated end-to-end.
Authoring
A poster is a file that default-exports a React component. The root element declares the canvas via w-[Npx] - the renderer measures it and crops the screenshot to that exact box. Add h-[Npx] only if you need a fixed aspect (story format, OG image); otherwise let the height grow with content.
import { AreaChart, Area, XAxis, YAxis } from "recharts";
import { SparklesIcon } from "lucide-react";
const data = Array.from({ length: 24 }, (_, i) => ({
h: i,
v: 50 + Math.sin(i * 0.5) * 20,
}));
export default function App() {
return (
<div className="w-[1200px] p-10 bg-black text-white">
<SparklesIcon className="h-6 w-6" />
<h1 className="mt-4 text-5xl font-black">Hello</h1>
<div style={{ width: "100%", height: 300 }} className="mt-8">
<AreaChart data={data} width={1100} height={300}>
<XAxis dataKey="h" />
<YAxis />
<Area dataKey="v" stroke="#22d3ee" fill="#22d3ee40" />
</AreaChart>
</div>
</div>
);
}In the box: React 19, Tailwind (via CDN), Recharts, lucide-react, Inter + Source Serif 4 + JetBrains Mono (loaded via Google Fonts so exports are consistent across machines).
Authoring surface depends on the engine. With --engine chrome,
anything that renders in Chrome renders here — hooks, context, useState,
animations, SVG, CSS gradients, backdrop-filter, fonts, the lot. With the
default takumi engine, the CSS subset is large but not complete: most
posters work as-is; a few patterns (gradient text, multi-layer absolute
blurs, SVG <text> inside serialized images) need authoring tweaks.
Engines
poster export runs through one of two engines. Pick with --engine or
the engine option on the SDK.
takumi — default
A pure-Rust headless renderer (taffy + parley + skrifa + resvg) shipped
as a NAPI native module. Real Tailwind v4 expands every class server-side
before handoff; Google Fonts CSS is fetched once and cached at
~/.cache/poster/fonts/; bare import specifiers auto-resolve through
esm.sh and cache at ~/.cache/poster/modules/. Output is 2x physical
pixels for retina parity.
- No browser, no download, fast.
- PNG only. PDF / SVG / JPG / WebP require
--engine chrome. - Subset of CSS. Most posters work; some patterns need authoring
tweaks (gradient text needs
display: inline-block+WebkitTextFillColor: transparent;overflow-hiddenon auto-height cards can clip).
chrome — opt-in
Puppeteer drives a real Chromium and screenshots the rendered DOM (DSF 2 for retina). What you see in Chrome is what lands in the file, pixel-for-pixel.
- Every format. PNG, JPG, WebP, PDF (vector text), SVG (snapDOM).
- Full CSS. Anything Chrome renders works.
- Needs Chrome. Uses system Chrome / Brave / Edge if present;
otherwise downloads
chrome-headless-shell(~80 MB) on demand.
Browser resolution (chrome engine only):
--browser <path>if given- System Chrome / Brave / Edge / Chromium
- Cached
chrome-headless-shellfrom@puppeteer/browsers - Auto-install (~80 MB) if
--install-browseris passed
Format support matrix
| Format | takumi | chrome | Notes |
|---|---|---|---|
| png | ✓ default | ✓ | Lossless, DSF 2. Transparent unless poster paints a background. |
| jpg | — | ✓ | Quality 100. White background from the shell's body. |
| webp | — | ✓ | Quality 100. Smallest raster at comparable fidelity. |
| pdf | — | ✓ | Vector text + SVG, raster images at 96 DPI. Text stays selectable. |
| svg | — | ✓ | Scalable, fonts embedded. Captured via snapDOM in-page. |
Browser download
The postinstall never downloads Chrome by default — the default engine is browserless. Opt in explicitly when you want the Chrome engine ready out of the box:
POSTER_INSTALL_BROWSER=1 npm install -g poster-ai # prefetch ~80 MB
POSTER_SKIP_BROWSER_DOWNLOAD=1 npm install poster-ai # always skipIf the download fails (offline, proxy, etc.), install still succeeds.
Run poster export --engine chrome --install-browser later to retry,
or install Chrome / Brave / Edge through your OS and skip the bundled
binary entirely.
For agents
- Every CLI command supports
--jsonfor machine-readable output. - Entry
-reads TSX from stdin, so a single call produces an image with no filesystem scaffolding:echo '...' | poster export - -o out.png. - Saved
.poster/<name>.tsxlets the agent iterate on its own output. - The SDK (
import { Poster }) is pure: discriminated input, data out, errors throw. No process control, no ambient logging. - The renderer auto-fits the canvas to whatever the root
w-[Npx]declares, so agents don't have to think about viewport sizes.
For pi users,
pi-poster registers a
poster_render tool plus a comprehensive poster skill so the agent
knows the layout grammar, color systems, font floor, and signature
patterns up front. Most of the gallery in examples/ was generated
through that loop - every example has a paired .txt sidecar
(vinyl.png + vinyl.tsx + vinyl.txt) so you can see exactly what
input produced what output.
Requirements
- Node 18+
- macOS, Linux, or Windows
- For PNG (default engine): nothing else — Takumi ships as a NAPI module.
- For PDF / SVG / JPG / WebP (
--engine chrome): Chrome / Brave / Edge installed, or ~80 MB for the fallbackchrome-headless-shell.
License
MIT.
