Satori
Satori (悟り) is a Japanese Buddhist term for awakening, “comprehension; understanding”. In the context of web development, Satori is a powerful library for generating SVG images from HTML and CSS.
Overview
Satori is a library that converts HTML and CSS into SVG images. It’s particularly useful for generating social media images, thumbnails, and other visual content programmatically.
Key Features
1. HTML to SVG Conversion
- Converts HTML markup to SVG images
- Supports CSS styling and layout
- Generates high-quality vector graphics
2. Font Support
- Built-in font rendering
- Custom font loading
- Multiple font weight support
3. CSS Layout Engine
- Flexbox support
- CSS Grid support
- Responsive design capabilities
4. Performance
- Fast rendering
- Memory efficient
- Optimized for server-side generation
Installation
npm install satoriBasic Usage
Simple Example
import satori from 'satori'
import { join } from 'path'
import { readFileSync } from 'fs'
// Load a font
const fontPath = join(process.cwd(), 'fonts', 'Inter-Regular.ttf')
const fontData = readFileSync(fontPath)
// Define your HTML
const html = `
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; height: 100%; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
<h1 style="color: white; font-size: 48px; margin: 0;">Hello Satori!</h1>
<p style="color: rgba(255, 255, 255, 0.8); font-size: 24px; margin: 16px 0 0 0;">Generate beautiful images from HTML</p>
</div>
`
// Generate SVG
const svg = await satori(html, {
width: 1200,
height: 630,
fonts: [
{
name: 'Inter',
data: fontData,
weight: 400,
style: 'normal',
},
],
})
console.log(svg)Advanced Example with Custom Components
import satori from 'satori'
import { join } from 'path'
import { readFileSync } from 'fs'
// Load fonts
const interRegular = readFileSync(join(process.cwd(), 'fonts', 'Inter-Regular.ttf'))
const interBold = readFileSync(join(process.cwd(), 'fonts', 'Inter-Bold.ttf'))
// Create a blog post card
const createBlogCard = (title, excerpt, author, date) => `
<div style="display: flex; flex-direction: column; width: 800px; height: 400px; background: white; border-radius: 16px; padding: 40px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);">
<div style="display: flex; flex-direction: column; flex: 1;">
<h1 style="font-size: 32px; font-weight: bold; color: #1a1a1a; margin: 0 0 16px 0; line-height: 1.2;">
${title}
</h1>
<p style="font-size: 18px; color: #666; margin: 0; line-height: 1.5; flex: 1;">
${excerpt}
</p>
</div>
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 24px; padding-top: 24px; border-top: 1px solid #eee;">
<div style="display: flex; align-items: center;">
<div style="width: 40px; height: 40px; border-radius: 50%; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); margin-right: 12px;"></div>
<span style="font-size: 16px; color: #1a1a1a; font-weight: 500;">${author}</span>
</div>
<span style="font-size: 14px; color: #999;">${date}</span>
</div>
</div>
`
// Generate the card
const svg = await satori(createBlogCard(
'Building Modern Web Applications',
'Learn how to build scalable and maintainable web applications using modern technologies and best practices.',
'John Doe',
'January 15, 2024'
), {
width: 800,
height: 400,
fonts: [
{
name: 'Inter',
data: interRegular,
weight: 400,
style: 'normal',
},
{
name: 'Inter',
data: interBold,
weight: 700,
style: 'normal',
},
],
})Font Configuration
Loading Custom Fonts
import { readFileSync } from 'fs'
import { join } from 'path'
// Load multiple font weights
const fonts = [
{
name: 'Inter',
data: readFileSync(join(process.cwd(), 'fonts', 'Inter-Regular.ttf')),
weight: 400,
style: 'normal',
},
{
name: 'Inter',
data: readFileSync(join(process.cwd(), 'fonts', 'Inter-Bold.ttf')),
weight: 700,
style: 'normal',
},
{
name: 'Inter',
data: readFileSync(join(process.cwd(), 'fonts', 'Inter-Italic.ttf')),
weight: 400,
style: 'italic',
},
]
const svg = await satori(html, {
width: 1200,
height: 630,
fonts,
})Using Google Fonts
// Fetch Google Fonts
const fetchGoogleFont = async (family, weight = 400) => {
const url = `https://fonts.googleapis.com/css2?family=${family}:wght@${weight}&display=swap`
const response = await fetch(url)
const css = await response.text()
// Extract font URL from CSS
const fontUrl = css.match(/src: url\((.+)\)/)?.[1]
if (!fontUrl) throw new Error('Font URL not found')
const fontResponse = await fetch(fontUrl)
return await fontResponse.arrayBuffer()
}
const interFont = await fetchGoogleFont('Inter', 400)
const fonts = [
{
name: 'Inter',
data: interFont,
weight: 400,
style: 'normal',
},
]CSS Support
Supported Properties
Satori supports most common CSS properties:
const html = `
<div style="
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 40px;
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
">
<h1 style="
color: white;
font-size: 48px;
font-weight: bold;
margin: 0 0 16px 0;
text-align: center;
line-height: 1.2;
">Welcome to Satori</h1>
<p style="
color: rgba(255, 255, 255, 0.8);
font-size: 24px;
margin: 0;
text-align: center;
line-height: 1.5;
">Generate beautiful images from HTML and CSS</p>
</div>
`Flexbox Layout
const html = `
<div style="display: flex; width: 100%; height: 100%;">
<div style="flex: 1; background: #f0f0f0; padding: 20px;">
<h2>Left Panel</h2>
<p>This is the left side content.</p>
</div>
<div style="flex: 2; background: #e0e0e0; padding: 20px;">
<h2>Main Content</h2>
<p>This is the main content area.</p>
</div>
<div style="flex: 1; background: #d0d0d0; padding: 20px;">
<h2>Right Panel</h2>
<p>This is the right side content.</p>
</div>
</div>
`