satori

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Satori

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(...)
returns an SVG string.
width
and
height
set the canvas. At least one font is required whenever any text is rendered (see Fonts).
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(...)
返回一个SVG字符串。
width
height
用于设置画布尺寸。只要渲染文本,就至少需要指定一种字体(详见字体部分)。

Constraints

约束条件

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
    display: 'flex'
    (or
    display: 'none'
    ).
    Satori is Flexbox only. A
    div
    with multiple children and no explicit display will throw. Default to putting
    display: 'flex'
    on every container. Single text children are tolerated, but being explicit is safest.
  • Default
    flexDirection
    is
    row
    , not
    column
    .
    This is the opposite of how people mentally stack divs. Set
    flexDirection: 'column'
    whenever you want vertical stacking.
  • Padding and margin shorthand need explicit units on every value.
    padding: '0 36'
    throws
    Expected length unit
    . Write
    padding: '0px 36px'
    , and
    '0px 36px 36px 36px'
    for the four value form. A single bare number like
    padding: 36
    is fine, because Satori treats a lone number as px.
  • Use
    flex
    layout for everything, including overlap.
    For overlapping or precisely placed elements, use
    position: 'absolute'
    with
    top
    /
    left
    /
    right
    /
    bottom
    on a
    position: 'relative'
    parent.
  • Never put HTML entity references in text. Satori does not decode them, so
    publish&#8209;ready
    renders the literal characters
    &#8209;
    on the image instead of a non-breaking hyphen. This applies to numeric (
    &#8209;
    ,
    &#160;
    ) and named (
    &nbsp;
    ,
    &amp;
    ,
    &mdash;
    ) entities alike. Write the actual Unicode character directly in the string instead —
    publish‑ready
    (or the literal glyph
    publish‑ready
    ) for a non-breaking hyphen,
     
    for a non-breaking space,
    &
    for an ampersand,
    for an em dash.
以下行为最常导致输出错误或运行时异常。编写标记前请务必注意这些约束。
  • 所有包含多个子元素的元素必须声明
    display: 'flex'
    (或
    display: 'none'
    )。
    Satori仅支持Flexbox布局。如果一个div包含多个子元素但未显式设置display,会抛出错误。默认情况下,应为所有容器添加
    display: 'flex'
    。单个文本子元素可以兼容,但显式设置更安全。
  • 默认
    flexDirection
    row
    (横向排列),而非
    column
    (纵向排列)。
    这与人们习惯的div堆叠方式相反。如需纵向堆叠元素,请设置
    flexDirection: 'column'
  • 内边距(padding)和外边距(margin)的简写形式需要为每个值添加明确的单位。
    padding: '0 36'
    会抛出
    Expected length unit
    错误。请写成
    padding: '0px 36px'
    ,四值形式则写成
    '0px 36px 36px 36px'
    。单个纯数字(如
    padding: 36
    )是可行的,因为Satori会将单独的数字视为px单位。
  • 所有布局都使用flex,包括元素重叠场景。 如需实现元素重叠或精确定位,请在
    position: 'relative'
    的父元素内,为子元素设置
    position: 'absolute'
    并配合
    top
    /
    left
    /
    right
    /
    bottom
    属性。
  • 请勿在文本中使用HTML实体引用。 Satori不会解析它们,因此
    publish&#8209;ready
    会在图片上渲染出字面字符
    &#8209;
    ,而非非断字连字符。这适用于数字实体(如
    &#8209;
    &#160;
    )和命名实体(如
    &nbsp;
    &amp;
    &mdash;
    )。请直接在字符串中写入实际的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.md
.
Satori实现了CSS的一个子集。默认假设未在支持列表中的内容均不支持,使用前请务必验证。如需查看包含允许值和默认值的完整矩阵,请阅读
references/css-support.md

Supported

支持的CSS

CategoryProperties
Layout
display
(
flex
,
contents
,
none
),
position
(
relative
,
static
,
absolute
),
top
/
right
/
bottom
/
left
,
width
/
height
, min/max width/height,
overflow
(
visible
,
hidden
)
Flex
flexDirection
,
flexWrap
,
flexGrow
,
flexShrink
,
flexBasis
,
alignItems
,
alignContent
,
alignSelf
,
justifyContent
,
gap
Box
margin
,
padding
,
border
(width,
solid
/
dashed
style, color, shorthand),
borderRadius
,
boxSizing
,
boxShadow
,
opacity
Color and background
color
,
backgroundColor
(single value),
backgroundImage
(
linear-gradient
,
repeating-linear-gradient
,
radial-gradient
,
repeating-radial-gradient
,
url
),
backgroundPosition
,
backgroundSize
(
cover
,
contain
,
auto
, two-value),
backgroundClip
(
border-box
,
text
),
backgroundRepeat
Text
fontFamily
,
fontSize
,
fontWeight
,
fontStyle
,
textAlign
,
textTransform
,
textOverflow
(
clip
,
ellipsis
),
textDecoration
,
textShadow
,
lineHeight
,
letterSpacing
,
whiteSpace
,
wordBreak
,
textWrap
(
wrap
,
balance
),
textIndent
,
tabSize
,
lineClamp
Transform and effects
transform
(translate, rotate, scale, skew),
transformOrigin
,
filter
,
clipPath
, mask (
maskImage
,
maskPosition
,
maskSize
,
maskRepeat
),
objectFit
,
objectPosition
,
WebkitTextStroke
Variables
--name
declarations and
var(--name, fallback)
usage, including inheritance and nesting
类别属性
布局
display
flex
contents
none
)、
position
relative
static
absolute
)、
top
/
right
/
bottom
/
left
width
/
height
、最小/最大宽高、
overflow
visible
hidden
Flex布局
flexDirection
flexWrap
flexGrow
flexShrink
flexBasis
alignItems
alignContent
alignSelf
justifyContent
gap
盒模型
margin
padding
border
(宽度、
solid
/
dashed
样式、颜色、简写形式)、
borderRadius
boxSizing
boxShadow
opacity
颜色与背景
color
backgroundColor
(单一值)、
backgroundImage
linear-gradient
repeating-linear-gradient
radial-gradient
repeating-radial-gradient
url
)、
backgroundPosition
backgroundSize
cover
contain
auto
、双值形式)、
backgroundClip
border-box
text
)、
backgroundRepeat
文本
fontFamily
fontSize
fontWeight
fontStyle
textAlign
textTransform
textOverflow
clip
ellipsis
)、
textDecoration
textShadow
lineHeight
letterSpacing
whiteSpace
wordBreak
textWrap
wrap
balance
)、
textIndent
tabSize
lineClamp
变换与效果
transform
(translate、rotate、scale、skew)、
transformOrigin
filter
clipPath
、mask(
maskImage
maskPosition
maskSize
maskRepeat
)、
objectFit
objectPosition
WebkitTextStroke
变量
--name
声明和
var(--name, fallback)
用法,包括继承和嵌套

Unsupported

不支持的CSS

Property or featureNotes and workaround
z-index
No stacking contexts. Elements paint in document order, so later siblings render on top. Reorder markup to control layering.
calc()
Precompute values in JavaScript before they reach the style object.
currentColor
outside
color
Resolves only for the
color
property. Set explicit values for borders, backgrounds, and fills.
3D transformsNot supported. Use 2D translate, rotate, scale, and skew only.
min-content
,
max-content
,
fit-content
Not supported for min/max width and height.
flexBasis: auto
Not supported. Use an explicit basis or rely on width/height.
Interactive or resource elementsNo
<input>
,
<style>
,
<script>
, or
<link>
. Only static, visible elements render.
<img>
alt
, SVG
<title>
Render as visible text on the image. Omit
alt
; strip
<title>
from inline SVG (see Images and Inline SVG).
WOFF2 fontsConvert to TTF, OTF, or WOFF (see Fonts).
AVIF / WebP imagesConvert to PNG or JPEG (see Images).
Kerning, ligatures, OpenType featuresAdvanced typography is not supported.
RTL languagesNot supported.
属性或特性说明与替代方案
z-index
不支持堆叠上下文。元素按照文档顺序绘制,因此后续的兄弟元素会渲染在上方。可通过调整标记顺序来控制层级。
calc()
在传入样式对象前,先在JavaScript中预先计算好值。
currentColor
color
属性外的场景)
仅在
color
属性中生效。为边框、背景和填充设置明确的值。
3D变换不支持。仅使用2D的translate、rotate、scale和skew。
min-content
max-content
fit-content
不支持用于最小/最大宽高。
flexBasis: auto
不支持。使用明确的基准值或依赖width/height。
交互或资源元素不支持
<input>
<style>
<script>
<link>
。仅渲染静态、可见元素。
<img>
alt
属性、SVG的
<title>
会作为可见文本渲染在图片上。省略
alt
属性;从内联SVG中移除
<title>
(详见图片与内联SVG)。
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
<input>
, no
<style>
, no
<script>
, no
<link>
. 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
<div>
,
<span>
,
<img>
,
<svg>
, and text.
Satori仅支持静态、可见元素。不支持交互或加载资源的元素:无
<input>
、无
<style>
、无
<script>
、无
<link>
。输出效果无法保证与浏览器完全像素一致,因为Satori运行的是基于SVG 1.1的自研布局引擎。请尽量使用
<div>
<span>
<img>
<svg>
和文本。

Inline SVG

内联SVG

Inline
<svg>
works and is the reliable way to place vector logos and icons. Keep the markup well formed: include a
viewBox
, and use explicit
width
/
height
. Grouping (
<g transform=...>
),
fill-rule
, and paths without fills render correctly.
Strip any
<title>
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.
内联
<svg>
可以正常工作,是放置矢量logo和图标可靠方式。请确保标记格式正确:包含
viewBox
,并设置明确的
width
/
height
。分组(
<g transform=...>
)、
fill-rule
和无填充的路径都能正确渲染。
传入前请从SVG标记中移除所有
<title>
元素。
Satori会将其视为文本内容,因此标题可能会作为可见文字泄露到渲染结果中。从图标库获取的logo通常会包含该元素,请检查并移除。

Images

图片

Use
<img>
and set
width
and
height
explicitly so layout is stable:
jsx
<img src="https://picsum.photos/200/300" width={200} height={300} />
With
backgroundImage: url(...)
, the image stretches to fit the element unless you set
backgroundSize
. When the SVG will be rasterized to PNG afterward, prefer a base64 data URI (or a Buffer/ArrayBuffer) as
src
so Satori does not perform extra network I/O.
Only 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
sharp
), and remember that a modern URL ending in
.jpg
may still serve WebP via content negotiation, so convert the bytes rather than trusting the extension.
Do not put an
alt
attribute on
<img>
.
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.
使用
<img>
并明确设置
width
height
以保证布局稳定:
jsx
<img src="https://picsum.photos/200/300" width={200} height={300} />
使用
backgroundImage: url(...)
时,除非设置
backgroundSize
,否则图片会拉伸以适配元素。当SVG后续将被光栅化为PNG时,优先使用base64数据URI(或Buffer/ArrayBuffer)作为
src
,这样Satori无需执行额外的网络I/O。
仅PNG、JPEG和GIF能可靠解码。AVIF和WebP无法正常工作,会静默渲染失败。传入前请将它们转换为PNG或JPEG(例如使用
sharp
工具),并注意现代URL中以
.jpg
结尾的文件可能仍会通过内容协商返回WebP,因此请转换字节内容而非信任文件扩展名。
请勿在
<img>
上添加
alt
属性。
Satori会将其视为文本内容,因此属性值可能会作为可见文字泄露到画布上。请省略该属性;输出是静态图片,添加它没有任何意义。

Fonts

字体

Any rendered text requires at least one font. Satori accepts TTF, OTF, and WOFF. WOFF2 is not supported. Pass font data as
ArrayBuffer
(web) or
Buffer
(Node.js):
jsx
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
fontFamily
. Define fonts once and reuse the object across renders for better performance rather than rebuilding it per call.
Advanced typography (kerning, ligatures, other OpenType features) is not supported, and RTL languages are not supported.
任何要渲染的文本都至少需要一种字体。Satori支持TTF、OTF和WOFF格式。不支持WOFF2。请以
ArrayBuffer
(Web环境)或
Buffer
(Node.js环境)形式传入字体数据:
jsx
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
graphemeImages
, where each image is sized to the current font size as a square:
jsx
await satori(<div style={{ display: 'flex' }}>Ship it 🚀</div>, {
  ...,
  graphemeImages: { '🚀': 'https://cdnjs.cloudflare.com/.../1f680.svg' },
})
文本字符从提供的字体中渲染;表情符号不会自动支持。请使用
graphemeImages
将特定字符映射到图片源,其中每个图片都会被调整为当前字体大小的正方形:
jsx
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
lang
on an element to force a locale, for example
<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
loadAdditionalAsset(code, segment)
.
code
is the detected language code, or
'emoji'
, or
'unknown'
. Return a data URI for emoji, or font data for text:
jsx
loadAdditionalAsset: async (code, segment) => {
  if (code === 'emoji') return `data:image/svg+xml;base64,...`
  return loadFontFromSystem(code)
}
当文本片段需要预先未提供的字体或表情符号图片时,Satori会调用
loadAdditionalAsset(code, segment)
code
为检测到的语言代码、
'emoji'
'unknown'
。表情符号请返回数据URI,文本请返回字体数据:
jsx
loadAdditionalAsset: async (code, segment) => {
  if (code === 'emoji') return `data:image/svg+xml;base64,...`
  return loadFontFromSystem(code)
}

Output and rendering options

输出与渲染选项

  • embedFont
    (default
    true
    ): text is emitted as
    <path>
    with the glyph outlines inlined, so downstream tools need no font files. Set
    embedFont: false
    to emit
    <text>
    instead (smaller output, but the renderer must have the font).
  • pointScaleFactor
    : passed through to Yoga to control how layout values round to the pixel grid; raise it for crisper output on high-DPI targets.
  • debug: true
    : draws bounding boxes, which is the fastest way to see why layout is off.
  • embedFont
    (默认
    true
    ):文本会以内联字形轮廓的
    <path>
    形式输出,因此下游工具无需字体文件。设置
    embedFont: false
    可改为输出
    <text>
    (输出体积更小,但渲染器必须具备对应的字体)。
  • pointScaleFactor
    :传递给Yoga引擎,用于控制布局值如何四舍五入到像素网格;在高DPI目标上提高该值可获得更清晰的输出。
  • 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:
  1. Did every multi-child container get
    display: 'flex'
    ?
    Missing display is the most common error and silent misalignment.
  2. Is the direction right? Remember the default is
    row
    . Vertical stacks need
    flexDirection: 'column'
    .
  3. Did a shorthand value lose its unit?
    Expected length unit
    means a
    padding
    /
    margin
    /
    border
    value needs
    px
    or
    %
    .
  4. Is the property actually supported? Check
    references/css-support.md
    . Unsupported properties are ignored or throw rather than approximated.
  5. Are you relying on
    z-index
    ,
    calc
    , or
    currentColor
    off the
    color
    property?
    None of those work; reorder markup, precompute, or set explicit values.
  6. Turn on
    debug: true
    to see bounding boxes and confirm the layout tree.
当输出效果异常时,请按以下顺序排查,因为这些情况覆盖了绝大多数问题:
  1. 所有多子元素容器都设置了
    display: 'flex'
    吗?
    缺失display设置是最常见的错误,会导致静默对齐异常。
  2. 排列方向是否正确? 记住默认方向是
    row
    。纵向堆叠需要设置
    flexDirection: 'column'
  3. 简写值是否遗漏了单位?
    Expected length unit
    错误意味着
    padding
    /
    margin
    /
    border
    值需要添加
    px
    %
    单位。
  4. 该属性是否真的被支持? 请查看
    references/css-support.md
    。不支持的属性会被忽略或抛出错误,而非近似处理。
  5. 是否依赖了
    z-index
    calc
    color
    属性外的
    currentColor
    这些都无法工作;请调整标记顺序、预先计算值或设置明确的值。
  6. **开启
    debug: true
    **以查看边界框,确认布局树是否正确。

Reference files

参考文件

  • references/css-support.md
    — 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矩阵,以及全局限制说明。当你不确定某个属性或值是否被支持时,请阅读该文件。