Satori SVG Logos: Keep Artwork in a Branded Card
Embed an SVG logo in a Satori card with explicit dimensions and self-contained image data. Inspect the nested artwork, then render a separate PNG delivery file.
On this page
To place an SVG logo in a Satori card, pass it as an image source, set the image width and height, and keep the original SVG beside the generated card. In the example below, a base64 SVG becomes a nested image in Satori's output. It does not become a new collection of editable logo paths in the outer card.
Satori creates SVG from a limited set of HTML and CSS. Its official image guidance recommends explicit dimensions and self-contained image data when a later renderer will produce PNG. That gives this job two separate outputs: a composed SVG card and a raster delivery copy.
Keep three files with different jobs
| File | What it contains | What to change there |
|---|---|---|
| Original logo SVG | The emblem's geometry and opening | Shape, color, source bounds |
| Satori card SVG | Card layout, lettering and an embedded logo image | Artwork box, placement and card content |
| Rendered PNG | The delivered card's pixels | Regenerate it after source changes |
An SVG <image> can reference another SVG, as MDN's image reference explains. An image element therefore does not automatically mean the embedded artwork is raster. Inspect its data type and decoded contents before diagnosing the card with the broader SVG raster-image checklist.

A complete local example
This starter uses the standard SVG-output import from Satori 0.35.0 and resvg-js 2.6.2. In an empty working folder, install the pinned packages:
npm install --save-exact satori@0.35.0 @resvg/resvg-js@2.6.2Create a fonts folder and download Abel-Regular.ttf and OFL.txt from the Google Fonts Abel directory. Abel is licensed under the SIL Open Font License 1.1; retain the accompanying notice and license when distributing the font with this example.
Save the following as starter.mjs and run node starter.mjs. It supplies the font to Satori, authors a hollow diamond, and makes two 600×320 cards with artwork boxes of 80×80 and 160×160. File URLs keep the font and output paths relative to the script.
import { readFile, writeFile } from 'node:fs/promises';
import satori from 'satori';
import { Resvg } from '@resvg/resvg-js';
const logo = `<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128"><path d="M64 8 L120 64 L64 120 L8 64 Z M64 42 L86 64 L64 86 L42 64 Z" fill="#176b5b" fill-rule="evenodd"/></svg>`;
const src = `data:image/svg+xml;base64,${Buffer.from(logo).toString('base64')}`;
const font = await readFile(new URL('./fonts/Abel-Regular.ttf', import.meta.url));
await writeFile(new URL('./logo-master.svg', import.meta.url), logo);
for (const size of [80, 160]) {
const svg = await satori({
type: 'div',
props: {
style: { display: 'flex', width: 600, height: 320,
backgroundColor: '#f6f2e8', color: '#152e29', fontFamily: 'Abel' },
children: [
{ type: 'img', props: { src, width: size, height: size,
style: { position: 'absolute', left: 24, top: 64 } } },
{ type: 'div', props: {
style: { position: 'absolute', left: 220, top: 112, fontSize: 42 },
children: 'CEDAR FIELD' } },
{ type: 'div', props: {
style: { position: 'absolute', left: 220, top: 168, fontSize: 20 },
children: 'A study in simple shapes' } },
],
},
}, {
width: 600, height: 320,
fonts: [{ name: 'Abel', data: font, weight: 400, style: 'normal' }],
});
const png = new Resvg(svg, { font: { loadSystemFonts: false } }).render().asPng();
await writeFile(new URL(`./card-${size}.svg`, import.meta.url), svg);
await writeFile(new URL(`./card-${size}.png`, import.meta.url), png);
}Buffer.from(logo).toString('base64') encodes the authored UTF-8 SVG bytes using Node's Buffer encoding API. The data:image/svg+xml;base64, prefix identifies the content. It changes the transport representation, not the logo geometry.
Satori's call returns the card SVG. The next line passes that SVG to the separate resvg-js PNG renderer. The starter has no remote image URL; it reads the local font before rendering. Test your own remote-fetch or deployment behavior separately.
What the two actual calls produced
We ran this exact starter with the pinned packages, inspected both output SVGs, and opened each SVG and PNG in a browser. All four card files loaded at 600×320. The lettering stayed in the same position while the emblem's box doubled in size.
Each card contained one outer <image> and two outer <path> elements. The two paths represented the lettering; they were not the diamond's outer and inner contours. Decoding the image's base64 data recovered the exact original SVG bytes: one path, zero image elements, and the same hollow diamond definition. The card had no live <text> elements in this run.
| Inspection | 80×80 artwork box | 160×160 artwork box |
|---|---|---|
| Card dimensions | 600×320 | 600×320 |
| Outer image placement | x24, y64 | x24, y64 |
| Embedded SVG | Original 128×128 master | Same master bytes |
| Exact solid-teal PNG bounds | 68×68 pixels | 138×138 pixels |
| Card pixel at opening center | Cream, alpha 255 | Cream, alpha 255 |
The paint-bound rows count pixels matching the emblem's exact solid teal color. They exclude antialiased edges. They are not the image rectangle dimensions: the original diamond spans source coordinates 8 through 120, so its mathematical extent would scale to 70 or 140 units within these boxes. The measured solid-color bounds were slightly smaller.
The original emblem's center was transparent, with alpha 0 when rendered by itself. In each final card PNG, that opening showed the opaque cream background: RGBA 246,242,232,255. A visible opening and a transparent final-card pixel are different requirements. If you need a transparent delivery background, change the card background and inspect the resulting alpha separately.
These are two calls using one original emblem at two sizes. They do not establish that every SVG feature works, that repeated remote-image jobs are fixed, or that all destinations render the card identically.
Change the artwork box without losing the source
For your own logo, replace the logo string with a self-contained SVG or read your master file before encoding it. Check its viewBox, width, height and opening first. Extra empty space inside those bounds can make the visible logo smaller than its assigned card box.
Set both image dimensions to match the intended aspect ratio. Our source is square and both boxes are square; the output image used preserveAspectRatio="none", which caused no aspect-ratio change here. This fixture does not test a rectangular logo forced into a square slot. Change one layout variable at a time, then inspect the actual SVG and delivery PNG.
Keep lettering outside the embedded logo when you want to change it independently. This example's card text becomes paths in the delivered SVG, but the words remain editable in the script. Font input, artwork input and composed output serve different jobs; the resvg font walkthrough covers a separate live-SVG-text rendering workflow.
Satori's HTML and CSS documentation describes its supported subset and limits. A browser screenshot of your existing page is not a promise that this layout engine will reproduce it exactly. Start with a small card and the ordinary satori import, then add the properties your design actually needs.
Prepare a logo that begins as pixels
If your only logo is a PNG or JPG and you need editable contours, prepare an SVG logo with PerfectVector, inspect the silhouette and inner openings, and keep that clean SVG as the master. Then embed it in your card and verify both artwork sizes and the final PNG. Vectorization prepares the artwork; Satori handles the card composition. An existing clean SVG can go straight to the embedding step. The vectorization guide explains the source-versus-output distinction.
Keep the master, script and font inputs after delivering the PNG. If you use a different rasterization stage, verify its sizing separately; the Sharp SVG-to-PNG example covers that renderer's dimensions.
FAQ
Does an SVG image become editable paths in the Satori card? In this example, the logo remains a nested SVG image. Its original one-path source is preserved inside the image data; the outer card paths are lettering. Keep the original master for geometry edits.
Why is the visible logo smaller than its width and height? The image box includes the source bounds. Our diamond leaves margins inside a 128×128 source, and exact-color pixel bounds also exclude antialiased edges. Inspect source whitespace and the assigned aspect ratio separately.
Does the logo's transparent hole make the card transparent? The hole lets the card background show through. Our cream background is opaque, so the PNG pixel at the opening has alpha 255. Test a transparent card background separately if your delivery requires it.
Does Satori return the PNG? This starter asks Satori for an SVG string, then renders that string to PNG with resvg-js. Keep those stages separate so you can inspect layout and raster delivery independently.
Sources
- Satori image, layout and font documentation — image dimensions, self-contained inputs, standard SVG output and supported HTML/CSS scope.
- Satori 0.35.0 release — the pinned version used in the original example.
- SVG image element — SVG images can reference other SVG files.
- Node Buffer encoding API — the starter's base64 encoding operation.
- resvg-js usage — the separate SVG-to-PNG rendering stage.
- Abel font license — the font's origin, notice and redistribution license.
For your own PNG or JPG logo, prepare an SVG master with PerfectVector and inspect its outline and inner openings. Keep the master, then check its placement in the card SVG and the delivered PNG at the sizes you intend to use.
More from the blog

MapLibre SVG Icons: Check Pixels and Symbol Size
Decode custom SVG artwork for a MapLibre symbol layer, control image density and icon size, and keep an editable master. Verify the displayed shape and opening.

p5.js SVG Images: Load, Scale, and Keep Your Source
Load an SVG in p5.js 2.3.4 with async setup, check its transparent opening at three Canvas sizes, and keep editable paths separate from the displayed image.
