- Notifications
You must be signed in to change notification settings - Fork307
Enlightened library to convert HTML and CSS to SVG
License
vercel/satori
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Satori: Enlightened library to convert HTML and CSS to SVG.
Note
To use Satori in your project to generate PNG images like Open Graph images and social cards, check out ourannouncement andVercel’s Open Graph Image Generation →
To use it in Next.js, take a look at theNext.js Open Graph Image Generation template →
Satori supports the JSX syntax, which makes it very straightforward to use. Here’s an overview of the basic usage:
// api.jsximportsatorifrom'satori'constsvg=awaitsatori(<divstyle={{color:'black'}}>hello, world</div>,{width:600,height:400,fonts:[{name:'Roboto',// Use `fs` (Node.js only) or `fetch` to read the font as Buffer/ArrayBuffer and provide `data` here.data:robotoArrayBuffer,weight:400,style:'normal',},],},)
Satori will render the element into a 600×400 SVG, and return the SVG string:
'<svg ...><path d="..." fill="black"></path></svg>'Under the hood, it handles layout calculation, font, typography and more, to generate a SVG that matches the exact same HTML and CSS in a browser.
Satori only accepts JSX elements that are pure and stateless. You can use a subset of HTMLelements (see section below), or custom React components, but React APIs such asuseState,useEffect,dangerouslySetInnerHTML are not supported.
If you don't have JSX transpiler enabled, you can simply passReact-elements-like objects that havetype,props.children andprops.style (and other properties too) directly:
awaitsatori({type:'div',props:{children:'hello, world',style:{color:'black'},},},options)
Satori supports a limited subset of HTML and CSS features, due to its special use cases. In general, only these static and visible elements and properties that are implemented.
For example, the<input> HTML element, thecursor CSS property are not in consideration. And you can't use<style> tags or external resources via<link> or<script>.
Also, Satori does not guarantee that the SVG will 100% match the browser-rendered HTML output since Satori implements its own layout engine based on theSVG 1.1 spec.
You can find the list of supported HTML elements and their preset styleshere.
You can use<img> to embed images. However,width, andheight attributes are recommended to set:
awaitsatori(<imgsrc="https://picsum.photos/200/300"width={200}height={300}/>,options)
When usingbackground-image, the image will be stretched to fit the element by default if you don't specify the size.
If you want to render the generated SVG to another image format such as PNG, it would be better to use base64 encoded image data (or buffer) directly asprops.src so no extra I/O is needed in Satori:
awaitsatori(<imgsrc="data:image/png;base64,..."width={200}height={300}/>,// Or src={arrayBuffer}, src={buffer}options)
Satori uses the same Flexboxlayout engine as React Native, and it’snot a complete CSS implementation. However, it supports a subset of the spec that covers most common CSS features:
| Property | Property Expanded | Supported Values | Example |
|---|---|---|---|
display | flex,contents,none, default toflex | ||
position | relative,static andabsolute, default torelative | ||
color | Supported | ||
margin | |||
marginTop | Supported | ||
marginRight | Supported | ||
marginBottom | Supported | ||
marginLeft | Supported | ||
| Position | |||
top | Supported | ||
right | Supported | ||
bottom | Supported | ||
left | Supported | ||
| Size | |||
width | Supported | ||
height | Supported | ||
| Min & max size | |||
minWidth | Supported except formin-content,max-content andfit-content | ||
minHeight | Supported except formin-content,max-content andfit-content | ||
maxWidth | Supported except formin-content,max-content andfit-content | ||
maxHeight | Supported except formin-content,max-content andfit-content | ||
border | |||
Width (borderWidth,borderTopWidth, ...) | Supported | ||
Style (borderStyle,borderTopStyle, ...) | solid anddashed, default tosolid | ||
Color (borderColor,borderTopColor, ...) | Supported | ||
Shorthand (border,borderTop, ...) | Supported, i.e.1px solid gray | ||
borderRadius | |||
borderTopLeftRadius | Supported | ||
borderTopRightRadius | Supported | ||
borderBottomLeftRadius | Supported | ||
borderBottomRightRadius | Supported | ||
| Shorthand | Supported, i.e.5px,50% / 5px | ||
| Flex | |||
flexDirection | column,row,row-reverse,column-reverse, default torow | ||
flexWrap | wrap,nowrap,wrap-reverse, default towrap | ||
flexGrow | Supported | ||
flexShrink | Supported | ||
flexBasis | Supported except forauto | ||
alignItems | stretch,center,flex-start,flex-end,baseline,normal, default tostretch | ||
alignContent | Supported | ||
alignSelf | Supported | ||
justifyContent | Supported | ||
gap | Supported | ||
| Font | |||
fontFamily | Supported | ||
fontSize | Supported | ||
fontWeight | Supported | ||
fontStyle | Supported | ||
| Text | |||
tabSize | Supported | ||
textAlign | start,end,left,right,center,justify, default tostart | ||
textTransform | none,lowercase,uppercase,capitalize, defaults tonone | ||
textOverflow | clip,ellipsis, defaults toclip | ||
textDecoration | Support line typesunderline andline-through, and stylesdotted,dashed,double,solid | Example | |
textShadow | Supported | ||
lineHeight | Supported | ||
letterSpacing | Supported | ||
whiteSpace | normal,pre,pre-wrap,pre-line,nowrap, defaults tonormal | ||
wordBreak | normal,break-all,break-word,keep-all, defaults tonormal | ||
textWrap | wrap,balance, defaults towrap | ||
| Background | |||
backgroundColor | Supported, single value | ||
backgroundImage | linear-gradient,repeating-linear-gradient,radial-gradient,repeating-radial-gradient,url, single value | ||
backgroundPosition | Support single value | ||
backgroundSize | Support two-value size i.e.10px 20% | ||
backgroundClip | border-box,text | ||
backgroundRepeat | repeat,repeat-x,repeat-y,no-repeat, defaults torepeat | ||
transform | |||
Translate (translate,translateX,translateY) | Supported | ||
| Rotate | Supported | ||
Scale (scale,scaleX,scaleY) | Supported | ||
Skew (skew,skewX,skewY) | Supported | ||
transformOrigin | Support one-value and two-value syntax (both relative and absolute values) | ||
objectFit | contain,cover,none, default tonone | ||
objectPosition | Support keywords:top,bottom,left,right,center, and combinations liketop left. Defaults tocenter. | ||
opacity | Supported | ||
boxSizing | Supported | ||
boxShadow | Supported | ||
overflow | visible andhidden, default tovisible | ||
filter | Supported | ||
clipPath | Supported | Example | |
lineClamp | Supported | Example | |
| Mask | |||
maskImage | linear-gradient(...),radial-gradient(...),url(...) | Example | |
maskPosition | Supported | Example | |
maskSize | Support two-value size i.e.10px 20% | Example | |
maskRepeat | repeat,repeat-x,repeat-y,no-repeat, defaults torepeat | Example | |
WebkitTextStroke | WebkitTextStrokeWidth | Supported | |
WebkitTextStrokeColor | Supported | ||
Note:
- Three-dimensional transforms are not supported.
- There is no
z-indexsupport in SVG. Elements that come later in the document will be painted on top. calcisn't supported.currentColorsupport is only available for thecolorproperty.
Advanced typography features such as kerning, ligatures and other OpenType features are not currently supported.
RTL languages are not supported either.
Satori currently supports three font formats: TTF, OTF and WOFF. Note that WOFF2 is not supported at the moment. You must specify the font if any text is rendered with Satori, and pass the font data as ArrayBuffer (web) or Buffer (Node.js):
awaitsatori(<divstyle={{fontFamily:'Inter'}}>Hello</div>,{width:600,height:400,fonts:[{name:'Inter',data:inter,weight:400,style:'normal',},{name:'Inter',data:interBold,weight:700,style:'normal',},],})
Multiple fonts can be passed to Satori and used infontFamily.
Tip
We recommend you define global fonts instead of creating a new object and pass it to satori for better performance, if your fonts do not change.Read it for more detail
To render custom images for specific graphemes, you can usegraphemeImages option to map the grapheme to an image source:
awaitsatori(<div>Next.js is 🤯!</div>,{ ...,graphemeImages:{'🤯':'https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/svg/1f92f.svg',},})
The image will be resized to the current font-size (both width and height) as a square.
Satori supports rendering text in different locales. You can specify the supported locales via thelang attribute:
awaitsatori(<divlang="ja-JP">骨</div>)
Same characters can be rendered differently in different locales, you can specify the locale when necessary to force it to render with a specific font and locale. Check outthis example to learn more.
Supported locales are exported as theLocale enum type.
Satori supports dynamically loading emoji images (grapheme pictures) and fonts. TheloadAdditionalAsset function will be called when a text segment is rendered but missing the image or font:
awaitsatori(<div>👋 你好</div>,{// `code` will be the detected language code, `emoji` if it's an Emoji, or `unknown` if not able to tell.// `segment` will be the content to render.loadAdditionalAsset:async(code:string,segment:string)=>{if(code==='emoji'){// if segment is an emojireturn`data:image/svg+xml;base64,...`}// if segment is normal textreturnloadFontFromSystem(code)}})
Satori can be directly used in browser, Node.js (>= 16), and Web Workers. It bundles its underlying WASM dependencies as base64-encoded strings and loads them at runtime.
If there is a limitation on dynamically loading WASM (e.g. Cloudflare Workers), you can use the Standalone Build which is mentioned below.
Satori's standalone build doesn't include Yoga's WASM binary by default, and you need to load it manually before using Satori.
First, you need to download theyoga.wasm binary fromSatori build and provide it yourself. Let's usefetch to load it directly from the CDN as an example:
importsatori,{init}from'satori/standalone'constres=awaitfetch('https://unpkg.com/satori/yoga.wasm')constyogaWasm=awaitres.arrayBuffer()awaitinit(yogaWasm)// Now you can use satori as usualconstsvg=awaitsatori(...)
Of course, you can also load theyoga.wasm file from your local disk viafs.readFile in Node.js or other methods.
By default, Satori renders the text as<path> in SVG, instead of<text>. That means it embeds the font path data as inlined information, so succeeding processes (e.g. render the SVG on another platform) don’t need to deal with font files anymore.
You can turn off this behavior by settingembedFont tofalse, and Satori will use<text> instead:
constsvg=awaitsatori(<divstyle={{color:'black'}}>hello, world</div>,{ ...,embedFont:false,},)
SetpointScaleFactor to control how layout values are rounded to the pixel grid. This parameter is passed directly toYoga’spointScaleFactor and improves rendering precision on high-DPI displays.
constsvg=awaitsatori(<divstyle={{color:'black'}}>hello, world</div>,{ ...,pointScaleFactor:2,},)
To draw the bounding box for debugging, you can passdebug: true as an option:
constsvg=awaitsatori(<divstyle={{color:'black'}}>hello, world</div>,{ ...,debug:true,},)
You can use theVercel OG Image Playground to test and report bugs of Satori. Please follow ourcontribution guidelines before opening a Pull Request.
- Shu Ding (@shuding_)
About
Enlightened library to convert HTML and CSS to SVG
Topics
Resources
License
Contributing
Security policy
Uh oh!
There was an error while loading.Please reload this page.
