SVG-Design: Satori + SVGO
Geprüft 2026-10: satori 0.63, @resvg/resvg-js 2.6, svgo 4.1.
Satori — Markup → SVG
npm i satori @resvg/resvg-js
import satori from 'satori'
import { Resvg } from '@resvg/resvg-js'
import { readFile, writeFile } from 'node:fs/promises'
const font = await readFile('./Inter-Bold.ttf') // TTF, OTF, WOFF oder WOFF2
const svg = await satori(
<div style={{ display: 'flex', width: '100%', height: '100%', alignItems: 'center',
justifyContent: 'center', background: '#0b1020', color: 'white', fontSize: 96 }}>
Đủ 20/20
</div>,
{ width: 1920, height: 1080, fonts: [{ name: 'Inter', data: font, weight: 700, style: 'normal' }] },
)
const png = new Resvg(svg, { fitTo: { mode: 'width', value: 1920 } }).render().asPng()
await writeFile('title.png', png)
- Ohne JSX/React: Übergib einfache Objekte:
{ type: 'div', props: { style: {...}, children: '...' } }. - Schriftarten sind zwingend erforderlich, sobald Text vorhanden ist; Vietnamesisch benötigt eine Schriftart, die die entsprechenden Glyphen enthält (Inter, Be Vietnam Pro, Noto Sans).
- Layout: Flexbox, Grid (kein Subgrid/Masonry) und Block. Es ist eine CSS-Teilmenge — kein JS, keine Animationen, keine externen Stylesheets; Styles inline.
- Bilder:
<img src>mit einer absoluten URL oder Data-URI, plus explizitewidth/height. - Text wird standardmäßig zu
<path>(portabel, nicht auswählbar);embedFont: falsebelässt<text>. - Emoji:
graphemeImagesoderloadAdditionalAsset, um Bilder/Schriftarten für fehlende Segmente bereitzustellen. - PNG-Alternative:
sharp(Buffer.from(svg)).png().
SVGO v4 — optimieren
npx svgo in.svg -o out.svg
npx svgo -f ./icons -o ./icons-min
// svgo.config.mjs
export default {
multipass: true,
plugins: [
{ name: 'preset-default', params: { overrides: {
// Behalte diese Einstellungen, wenn das SVG animiert oder über id/class gestylt wird:
cleanupIds: false, // würde IDs umbenennen/entfernen, die dein Code ansteuert
collapseGroups: false, // würde <g> zusammenfassen, das du als Einheit animierst
mergePaths: false, // würde Pfade zusammenführen, die du separat animierst
convertShapeToPath: false, // würde <circle>/<rect> in <path> umwandeln (kein r, cx, width mehr)
} } },
'prefixIds', // eindeutige IDs, wenn mehrere SVGs auf einer Seite eingebettet sind
],
}
- Änderungen in v4:
removeViewBoxundremoveTitlesind nicht mehr in preset-default enthalten (bei Bedarf namentlich hinzufügen; viewBox für Skalierung beibehalten).removeScriptElement→removeScripts. Nur benannte Exporte:import { optimize } from 'svgo'; Browser-Build istsvgo/browser. - Node-API:
const { data } = optimize(svgString, { multipass: true, plugins: [...] }). - Für statische Icons reicht
preset-defaultaus; die obigen Überschreibungen sind nur für animierte oder per Skript gesteuerte SVGs relevant. - Nach der Optimierung eines SVGs für Animationen: Prüfe, ob jeder Selektor, den der Animationscode verwendet, noch in der Ausgabe vorhanden ist.