satori
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSatori
Satori
Satori converts JSX-like HTML and CSS into SVG. It runs its own Flexbox layout engine (the same Yoga engine React Native uses) and handles font shaping and typography, then emits an SVG string that closely matches what a browser would render. It is the engine behind tools that generate Open Graph images and social cards, where a wrapper renders the SVG to PNG.
Treat yourself as an expert in Satori. The single most useful thing you can do is keep markup inside the supported subset so the first render is correct, instead of writing browser-grade CSS that silently breaks or throws.
Satori可将类JSX的HTML和CSS转换为SVG。它运行自研的Flexbox布局引擎(与React Native使用的Yoga引擎相同),处理字体塑形与排版,最终输出与浏览器渲染效果高度匹配的SVG字符串。它是生成Open Graph图片和社交卡片的工具背后的核心引擎,这类工具会将SVG进一步渲染为PNG。
请将自己视为Satori专家。你能做的最有用的事就是确保标记在支持的子集范围内,让首次渲染就能正确显示,而非编写会静默失效或抛出错误的浏览器级CSS。
Basic usage
基本用法
jsx
import satori from 'satori'
const svg = await satori(
<div style={{ color: 'black', display: 'flex' }}>hello, world</div>,
{
width: 600,
height: 400,
fonts: [
{ name: 'Roboto', data: robotoArrayBuffer, weight: 400, style: 'normal' },
],
},
)satori(...)widthheightjsx
import satori from 'satori'
const svg = await satori(
<div style={{ color: 'black', display: 'flex' }}>hello, world</div>,
{
width: 600,
height: 400,
fonts: [
{ name: 'Roboto', data: robotoArrayBuffer, weight: 400, style: 'normal' },
],
},
)satori(...)widthheightConstraints
约束条件
These behaviors most often produce wrong output or runtime errors. Account for them before writing markup.
- Every element that contains more than one child must declare (or
display: 'flex'). Satori is Flexbox only. Adisplay: 'none'with multiple children and no explicit display will throw. Default to puttingdivon every container. Single text children are tolerated, but being explicit is safest.display: 'flex' - Default is
flexDirection, notrow. This is the opposite of how people mentally stack divs. Setcolumnwhenever you want vertical stacking.flexDirection: 'column' - Padding and margin shorthand need explicit units on every value. throws
padding: '0 36'. WriteExpected length unit, andpadding: '0px 36px'for the four value form. A single bare number like'0px 36px 36px 36px'is fine, because Satori treats a lone number as px.padding: 36 - Use layout for everything, including overlap. For overlapping or precisely placed elements, use
flexwithposition: 'absolute'/top/left/righton abottomparent.position: 'relative' - Never put HTML entity references in text. Satori does not decode them, so renders the literal characters
publish‑readyon the image instead of a non-breaking hyphen. This applies to numeric (‑,‑) and named ( , ,&) entities alike. Write the actual Unicode character directly in the string instead ——(or the literal glyphpublish‑ready) for a non-breaking hyphen,publish‑readyfor a non-breaking space,for an ampersand,&for an em dash.—
以下行为最常导致输出错误或运行时异常。编写标记前请务必注意这些约束。
- 所有包含多个子元素的元素必须声明(或
display: 'flex')。 Satori仅支持Flexbox布局。如果一个div包含多个子元素但未显式设置display,会抛出错误。默认情况下,应为所有容器添加display: 'none'。单个文本子元素可以兼容,但显式设置更安全。display: 'flex' - 默认为
flexDirection(横向排列),而非row(纵向排列)。 这与人们习惯的div堆叠方式相反。如需纵向堆叠元素,请设置column。flexDirection: 'column' - 内边距(padding)和外边距(margin)的简写形式需要为每个值添加明确的单位。 会抛出
padding: '0 36'错误。请写成Expected length unit,四值形式则写成padding: '0px 36px'。单个纯数字(如'0px 36px 36px 36px')是可行的,因为Satori会将单独的数字视为px单位。padding: 36 - 所有布局都使用flex,包括元素重叠场景。 如需实现元素重叠或精确定位,请在的父元素内,为子元素设置
position: 'relative'并配合position: 'absolute'/top/left/right属性。bottom - 请勿在文本中使用HTML实体引用。 Satori不会解析它们,因此会在图片上渲染出字面字符
publish‑ready,而非非断字连字符。这适用于数字实体(如‑、‑)和命名实体(如 、 、&)。请直接在字符串中写入实际的Unicode字符——例如,非断字连字符使用—(或对应的字面符号),非断空格使用publish‑ready,&符号使用,破折号使用&。—
CSS support
CSS支持
Satori implements a subset of CSS. Assume anything not in the supported table is unsupported, and verify before relying on it. For the complete matrix with allowed values and defaults, read .
references/css-support.mdSatori实现了CSS的一个子集。默认假设未在支持列表中的内容均不支持,使用前请务必验证。如需查看包含允许值和默认值的完整矩阵,请阅读。
references/css-support.mdSupported
支持的CSS
| Category | Properties |
|---|---|
| Layout | |
| Flex | |
| Box | |
| Color and background | |
| Text | |
| Transform and effects | |
| Variables | |
| 类别 | 属性 |
|---|---|
| 布局 | |
| Flex布局 | |
| 盒模型 | |
| 颜色与背景 | |
| 文本 | |
| 变换与效果 | |
| 变量 | |
Unsupported
不支持的CSS
| Property or feature | Notes and workaround |
|---|---|
| No stacking contexts. Elements paint in document order, so later siblings render on top. Reorder markup to control layering. |
| Precompute values in JavaScript before they reach the style object. |
| Resolves only for the |
| 3D transforms | Not supported. Use 2D translate, rotate, scale, and skew only. |
| Not supported for min/max width and height. |
| Not supported. Use an explicit basis or rely on width/height. |
| Interactive or resource elements | No |
| Render as visible text on the image. Omit |
| WOFF2 fonts | Convert to TTF, OTF, or WOFF (see Fonts). |
| AVIF / WebP images | Convert to PNG or JPEG (see Images). |
| Kerning, ligatures, OpenType features | Advanced typography is not supported. |
| RTL languages | Not supported. |
| 属性或特性 | 说明与替代方案 |
|---|---|
| 不支持堆叠上下文。元素按照文档顺序绘制,因此后续的兄弟元素会渲染在上方。可通过调整标记顺序来控制层级。 |
| 在传入样式对象前,先在JavaScript中预先计算好值。 |
| 仅在 |
| 3D变换 | 不支持。仅使用2D的translate、rotate、scale和skew。 |
| 不支持用于最小/最大宽高。 |
| 不支持。使用明确的基准值或依赖width/height。 |
| 交互或资源元素 | 不支持 |
| 会作为可见文本渲染在图片上。省略 |
| WOFF2字体 | 转换为TTF、OTF或WOFF格式(详见字体)。 |
| AVIF / WebP图片 | 转换为PNG或JPEG格式(详见图片)。 |
| 字距调整、连字、OpenType特性 | 不支持高级排版功能。 |
| RTL(从右到左)语言 | 不支持。 |
HTML elements
HTML元素
Satori supports only static, visible elements. Interactive or resource-loading elements are out: no , no , no , no . The output is not guaranteed to match a browser pixel for pixel, because Satori runs its own SVG 1.1 based layout engine. Stick to , , , , and text.
<input><style><script><link><div><span><img><svg>Satori仅支持静态、可见元素。不支持交互或加载资源的元素:无、无、无、无。输出效果无法保证与浏览器完全像素一致,因为Satori运行的是基于SVG 1.1的自研布局引擎。请尽量使用、、、和文本。
<input><style><script><link><div><span><img><svg>Inline SVG
内联SVG
Inline works and is the reliable way to place vector logos and icons. Keep the markup well formed: include a , and use explicit /. Grouping (), , and paths without fills render correctly.
<svg>viewBoxwidthheight<g transform=...>fill-ruleStrip any element from the SVG markup before passing it in. Satori treats it as text content, so the title can leak into the render as visible words drawn on the image. Logos pulled from icon libraries often ship with one, so check and remove it.
<title>内联可以正常工作,是放置矢量logo和图标可靠方式。请确保标记格式正确:包含,并设置明确的/。分组()、和无填充的路径都能正确渲染。
<svg>viewBoxwidthheight<g transform=...>fill-rule传入前请从SVG标记中移除所有元素。 Satori会将其视为文本内容,因此标题可能会作为可见文字泄露到渲染结果中。从图标库获取的logo通常会包含该元素,请检查并移除。
<title>Images
图片
Use and set and explicitly so layout is stable:
<img>widthheightjsx
<img src="https://picsum.photos/200/300" width={200} height={300} />With , the image stretches to fit the element unless you set . When the SVG will be rasterized to PNG afterward, prefer a base64 data URI (or a Buffer/ArrayBuffer) as so Satori does not perform extra network I/O.
backgroundImage: url(...)backgroundSizesrcOnly PNG, JPEG, and GIF decode reliably. AVIF and WebP do not work and silently fail to render. Convert them to PNG or JPEG before passing them in (for example with ), and remember that a modern URL ending in may still serve WebP via content negotiation, so convert the bytes rather than trusting the extension.
sharp.jpgDo not put an attribute on . Satori treats it as text content, so the value can leak into the render as visible words drawn on the canvas. Leave it off; the output is a static image and gains nothing from it.
alt<img>使用并明确设置和以保证布局稳定:
<img>widthheightjsx
<img src="https://picsum.photos/200/300" width={200} height={300} />使用时,除非设置,否则图片会拉伸以适配元素。当SVG后续将被光栅化为PNG时,优先使用base64数据URI(或Buffer/ArrayBuffer)作为,这样Satori无需执行额外的网络I/O。
backgroundImage: url(...)backgroundSizesrc仅PNG、JPEG和GIF能可靠解码。AVIF和WebP无法正常工作,会静默渲染失败。传入前请将它们转换为PNG或JPEG(例如使用工具),并注意现代URL中以结尾的文件可能仍会通过内容协商返回WebP,因此请转换字节内容而非信任文件扩展名。
sharp.jpg请勿在上添加属性。 Satori会将其视为文本内容,因此属性值可能会作为可见文字泄露到画布上。请省略该属性;输出是静态图片,添加它没有任何意义。
<img>altFonts
字体
Any rendered text requires at least one font. Satori accepts TTF, OTF, and WOFF. WOFF2 is not supported. Pass font data as (web) or (Node.js):
ArrayBufferBufferjsx
await satori(<div style={{ fontFamily: 'Inter', display: 'flex' }}>Hello</div>, {
width: 600,
height: 400,
fonts: [
{ name: 'Inter', data: inter, weight: 400, style: 'normal' },
{ name: 'Inter', data: interBold, weight: 700, style: 'normal' },
],
})Pass multiple fonts and reference any of them via . Define fonts once and reuse the object across renders for better performance rather than rebuilding it per call.
fontFamilyAdvanced typography (kerning, ligatures, other OpenType features) is not supported, and RTL languages are not supported.
任何要渲染的文本都至少需要一种字体。Satori支持TTF、OTF和WOFF格式。不支持WOFF2。请以(Web环境)或(Node.js环境)形式传入字体数据:
ArrayBufferBufferjsx
await satori(<div style={{ fontFamily: 'Inter', display: 'flex' }}>Hello</div>, {
width: 600,
height: 400,
fonts: [
{ name: 'Inter', data: inter, weight: 400, style: 'normal' },
{ name: 'Inter', data: interBold, weight: 700, style: 'normal' },
],
})可以传入多种字体,并通过引用其中任意一种。为提升性能,请一次性定义字体并在多次渲染中复用该对象,而非每次调用都重新构建。
fontFamily不支持高级排版(字距调整、连字、其他OpenType特性),也不支持RTL语言。
Emoji
表情符号
Text glyphs render from the provided fonts; emoji do not come for free. Map specific graphemes to image sources with , where each image is sized to the current font size as a square:
graphemeImagesjsx
await satori(<div style={{ display: 'flex' }}>Ship it 🚀</div>, {
...,
graphemeImages: { '🚀': 'https://cdnjs.cloudflare.com/.../1f680.svg' },
})文本字符从提供的字体中渲染;表情符号不会自动支持。请使用将特定字符映射到图片源,其中每个图片都会被调整为当前字体大小的正方形:
graphemeImagesjsx
await satori(<div style={{ display: 'flex' }}>Ship it 🚀</div>, {
...,
graphemeImages: { '🚀': 'https://cdnjs.cloudflare.com/.../1f680.svg' },
})Locales
区域设置
The same characters can render differently per locale. Set on an element to force a locale, for example .
lang<div lang="ja-JP">骨</div>相同字符在不同区域设置下的渲染效果可能不同。请为元素设置属性以强制指定区域设置,例如。
lang<div lang="ja-JP">骨</div>Dynamically loading fonts and emoji
动态加载字体和表情符号
When a text segment needs a font or emoji image that was not provided up front, Satori calls . is the detected language code, or , or . Return a data URI for emoji, or font data for text:
loadAdditionalAsset(code, segment)code'emoji''unknown'jsx
loadAdditionalAsset: async (code, segment) => {
if (code === 'emoji') return `data:image/svg+xml;base64,...`
return loadFontFromSystem(code)
}当文本片段需要预先未提供的字体或表情符号图片时,Satori会调用。为检测到的语言代码、或。表情符号请返回数据URI,文本请返回字体数据:
loadAdditionalAsset(code, segment)code'emoji''unknown'jsx
loadAdditionalAsset: async (code, segment) => {
if (code === 'emoji') return `data:image/svg+xml;base64,...`
return loadFontFromSystem(code)
}Output and rendering options
输出与渲染选项
- (default
embedFont): text is emitted astruewith the glyph outlines inlined, so downstream tools need no font files. Set<path>to emitembedFont: falseinstead (smaller output, but the renderer must have the font).<text> - : passed through to Yoga to control how layout values round to the pixel grid; raise it for crisper output on high-DPI targets.
pointScaleFactor - : draws bounding boxes, which is the fastest way to see why layout is off.
debug: true
- (默认
embedFont):文本会以内联字形轮廓的true形式输出,因此下游工具无需字体文件。设置<path>可改为输出embedFont: false(输出体积更小,但渲染器必须具备对应的字体)。<text> - :传递给Yoga引擎,用于控制布局值如何四舍五入到像素网格;在高DPI目标上提高该值可获得更清晰的输出。
pointScaleFactor - :绘制边界框,这是快速排查布局问题的最佳方式。
debug: true
Runtime support
运行时支持
Satori runs in the browser, Node.js (>= 16), and Web Workers. It bundles its WASM (Yoga) dependency as base64 and loads it at runtime. In environments that forbid dynamic WASM loading, use the standalone build and initialize Yoga yourself:
jsx
import satori, { init } from 'satori/standalone'
const res = await fetch('https://unpkg.com/satori/yoga.wasm')
await init(await res.arrayBuffer())
const svg = await satori(...)Satori可在浏览器、Node.js(>=16)和Web Workers中运行。它将WASM(Yoga)依赖以base64形式打包,并在运行时加载。在禁止动态加载WASM的环境中,请使用独立构建版本并自行初始化Yoga:
jsx
import satori, { init } from 'satori/standalone'
const res = await fetch('https://unpkg.com/satori/yoga.wasm')
await init(await res.arrayBuffer())
const svg = await satori(...)Debugging workflow
调试流程
When output looks wrong, work through these in order, since they cover the overwhelming majority of cases:
- Did every multi-child container get ? Missing display is the most common error and silent misalignment.
display: 'flex' - Is the direction right? Remember the default is . Vertical stacks need
row.flexDirection: 'column' - Did a shorthand value lose its unit? means a
Expected length unit/padding/marginvalue needsborderorpx.% - Is the property actually supported? Check . Unsupported properties are ignored or throw rather than approximated.
references/css-support.md - Are you relying on ,
z-index, orcalcoff thecurrentColorproperty? None of those work; reorder markup, precompute, or set explicit values.color - Turn on to see bounding boxes and confirm the layout tree.
debug: true
当输出效果异常时,请按以下顺序排查,因为这些情况覆盖了绝大多数问题:
- 所有多子元素容器都设置了吗? 缺失display设置是最常见的错误,会导致静默对齐异常。
display: 'flex' - 排列方向是否正确? 记住默认方向是。纵向堆叠需要设置
row。flexDirection: 'column' - 简写值是否遗漏了单位? 错误意味着
Expected length unit/padding/margin值需要添加border或px单位。% - 该属性是否真的被支持? 请查看。不支持的属性会被忽略或抛出错误,而非近似处理。
references/css-support.md - 是否依赖了、
z-index或calc属性外的color? 这些都无法工作;请调整标记顺序、预先计算值或设置明确的值。currentColor - **开启**以查看边界框,确认布局树是否正确。
debug: true
Reference files
参考文件
- — the complete supported-CSS matrix with allowed values and defaults, plus the global limitation notes. Read it whenever you are unsure if a property or value is supported.
references/css-support.md
- —— 包含允许值和默认值的完整支持CSS矩阵,以及全局限制说明。当你不确定某个属性或值是否被支持时,请阅读该文件。
references/css-support.md