A tiny, focused utility to make Satori accept dimension values when generating OG images from HTML or rendered Svelte components. It normalizes width/height attributes and inline styles to the numeric types Satori expects.
- Fixes Satori dimension validation for
<img>,<svg>, and<image>nodes produced bysatori-html - Coerces safe values like "78" or "78px" to numbers, preserving
autoand percentages - Recursive, handles nested children and style objects
- TypeScript-ready with clear types for the normalized node tree
- Drop-in utility to use directly between
toReactNode(...)andsatori(...)
- Common pattern inspiration: Dynamic OG image with SvelteKit and Satori (dev.to)
pnpm add @humanspeak/svelte-satori-fix satori satori-html @resvg/resvg-js
# or
npm i -S @humanspeak/svelte-satori-fix satori satori-html @resvg/resvg-jsThis mirrors the common Satori flow documented in the reference article.
// +server.ts (simplified)
import satori from 'satori'
import { Resvg } from '@resvg/resvg-js'
import { html as toReactNode } from 'satori-html'
const height = 630
const width = 1200
export const GET = async () => {
const node = toReactNode('<div style="color:red">Hello</div>')
const svg = await satori(node, { height, width })
const image = new Resvg(svg, { fitTo: { mode: 'width', value: width } }).render()
return new Response(image.asPng(), { headers: { 'content-type': 'image/png' } })
}When rendering a Svelte component (e.g., OG.svelte) to HTML, if it contains an <img> with width/height as strings (e.g., "78" or "78px"), Satori can throw validation errors. Example render step:
// Somewhere in your route/handler
const result = render(OG, {}) // result.body is HTML; you may also capture component CSSUse normalizeDimensionsForSatori right after toReactNode(...) and before satori(...).
import satori from 'satori'
import { Resvg } from '@resvg/resvg-js'
import { html as toReactNode } from 'satori-html'
import { normalizeDimensionsForSatori } from '@humanspeak/svelte-satori-fix'
// result from your component render
const result = render(OG, {})
const css = '' // if you collected CSS for the component, include it here
// Build a single HTML string, then convert to a Satori node
const element = normalizeDimensionsForSatori(toReactNode(`${result.body}<style>${css}</style>`))
// Proceed with Satori and PNG output
const svg = await satori(element, { height: 630, width: 1200 })
const image = new Resvg(svg, { fitTo: { mode: 'width', value: 1200 } }).render()
return new Response(image.asPng(), { headers: { 'content-type': 'image/png' } })- What this fixes: coerces
width/heightattributes and inlinestyle.width/style.heightto numbers (e.g.,78) where safe, which Satori expects for<img>,<svg>, and<image>. - Non-destructive: values like
autoand percentages (e.g.,50%) are preserved.
import { normalizeDimensionsForSatori } from '@humanspeak/svelte-satori-fix'normalizeDimensionsForSatori(node)- Accepts:
Node | Node[] | null(tree produced bysatori-html) - Returns: same node reference, normalized in-place
- Behavior:
- For
img,svg,image: coercesprops.width/props.heightwhen safe - For
style: coercesstyle.width/style.heightwhen safe - Recursively walks
childrenarrays/objects
- For
- Accepts:
If importing within this repo:
import { normalizeDimensionsForSatori } from '$lib/utils/main'- Works with both simple HTML templates and component-rendered HTML
- Place the normalization strictly between
toReactNode(...)andsatori(...)
MIT © Humanspeak, Inc.
Made with ❤️ by Humanspeak