SVG design: Satori + SVGO
Verificado em 2026-10: satori 0.63, @resvg/resvg-js 2.6, svgo 4.1.
Satori — marcação → 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 ou 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)
- Sem JSX/React, passe objetos simples:
{ type: 'div', props: { style: {...}, children: '...' } }. - Fontes são obrigatórias sempre que houver texto; vietnamita precisa de uma fonte que tenha os glifos (Inter, Be Vietnam Pro, Noto Sans).
- Layout: Flexbox, Grid (sem subgrid/masonry) e block. É um subconjunto de CSS — sem JS, sem animações, sem folhas de estilo externas; estilo inline.
- Imagens:
<img src>com URL absoluta ou data URI, maiswidth/heightexplícitos. - Texto vira
<path>por padrão (portátil, não selecionável);embedFont: falsemantém<text>. - Emoji:
graphemeImagesouloadAdditionalAssetpara fornecer imagens/fontes para segmentos ausentes. - Alternativa PNG:
sharp(Buffer.from(svg)).png().
SVGO v4 — otimizar
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: {
// mantenha estes quando o SVG for animado ou estilizado por id/class:
cleanupIds: false, // renomearia/removeria ids que seu código alvo
collapseGroups: false, // achataria <g> que você anima como uma unidade
mergePaths: false, // mesclaria caminhos que você anima separadamente
convertShapeToPath: false, // transformaria <circle>/<rect> em <path> (sem mais r, cx, width)
} } },
'prefixIds', // ids únicos quando vários SVGs são inline em uma página
],
}
- Mudanças v4:
removeViewBoxeremoveTitlenão estão mais no preset-default (adicione-os pelo nome se quiser; mantenha o viewBox para redimensionamento).removeScriptElement→removeScripts. Apenas exports nomeados:import { optimize } from 'svgo'; build para navegador ésvgo/browser. - API Node:
const { data } = optimize(svgString, { multipass: true, plugins: [...] }). - Para ícones estáticos,
preset-defaultsimples é suficiente; as substituições acima só importam para SVGs animados ou com script. - Após otimizar um SVG para animação, verifique se cada seletor que o código de animação usa ainda existe na saída.