Dynamic OG image with SvelteKit and Satori
Shivam Meena
Posted on October 20, 2022
Introduction
We all share links/url to our friends and we always see a beautiful image which is OpenGraph(OG) Image. OG images use to be static images and with time we got puppeteer which uses browser to take screenshot of your web page then you serve it from server. Recently, Vercel dropped a bomb @vercel/og with Satori which doesn't need a browser and you can generate Dynamic OG Images on the fly. As we know with every solution, we got some problems to be solved.
@vercel/og
is dependent on Satori
. Satori is a new libray that convert HTML and CSS to SVG. Satori only accepts two params those are ReactElement
(like JSX) and options
. In SvelteKit, we don't have a ReactElement
type syntax. Here, we need a html
to ReactElement
converter and Satori-html is what we need to do that.
Here, I'm going to explain a way to achieve Dynamic OG Images using html syntax. Read to the end, A pretty good surprise for all of You.
Flow to generate SVG
- We first need a
html template
which we need to convert into SVG. - We going to pass that
html template
tohtml
function fromsatori-html
, this will generate ourReactElement
syntax forhtml template
. - Now, we will pass our
ReactElement
like syntax andoptions
(options are defined in satori readme) to satori and this will return us with a SVG.
Note: SVG are not suppose to be served for OG Images (we use png format in OG Images) where we use SVG to PNG coverter Resvg.
Generate SVG from HTML Template
- Create a new SvelteKit Project
pnpm create svelte@next
- Then, In routes folder create a route with
+server.ts
or+server.js
. Which will going to serve our OG Image for our shareable link.
// +server.js
import satori from 'satori';
import {Resvg} from '@resvg/resvg-js';
import {html as toReactElement} from 'satori-html';
const fontFile = await fetch('https://og-playground.vercel.app/inter-latin-ext-700-normal.woff');
const fontData: ArrayBuffer = await fontFile.arrayBuffer();
const height = 630;
const width = 1200;
/** @type {import('./$types').RequestHandler} */
export const GET = async () => {
const html = toReactElement('<div style="color: red;">Hello, World!</div>')
const svg = await satori(html, {
fonts: [
{
name: 'Inter Latin',
data: fontData,
style: 'normal'
}
],
height,
width
});
const resvg = new Resvg(svg, {
fitTo: {
mode: 'width',
value: width
}
});
const image = resvg.render();
return new Response(image.asPng(), {
headers: {
'content-type': 'image/png'
}
});
};
In above code snippet,
First we import our libraries:
Satori
,Satori-html
and@resvg/resvg-js
.Then we are fetching our font and convert it into to arraybuffer.
Then we defined height, width for our svg.
Then we gonna write a Get Api for our endpoint, which will gonna return a response Object with png.
Next we defined our html syntax which is converted to
ReactElement
syntax usingtoReactElement
(import {html as toReactElement} from 'satori-html').Now, we gonna pass our
html
object to satori with required options. This will going to generate a SVG.
Options = {
fonts: [
{
name: 'Inter Latin',
data: fontData,
style: 'normal'
}
],
height,
width
}
- Now, we have a SVG we gonna use
resvg
to convert it into PNG. Then we are going to return our response.
const resvg = new Resvg(svg, {
fitTo: {
mode: 'width',
value: width
}
});
const image = resvg.render();
return new Response(image.asPng(), {
headers: {
'content-type': 'image/png'
}
});
This is a basic example of using satori in Svelte/SvelteKit. Few days ago,
geoffrich
released a post[Create dynamic social card images with Svelte components](https://geoffrich.net/posts/svelte-social-image/)
which is deep dive into this.
Surprise: @ethercorps/sveltekit-og
This is a library inspired from @vercel/og
and similar to it. I released@ethercorps/sveltekit-og
this morning and you can use it with normal html. Here I'm showing you two examples which returns an Image Response.
- Example:
ImageResponse
import { ImageResponse } from '@ethercorps/sveltekit-og';
import type { RequestHandler } from './$types';
const template = `
<div tw="bg-gray-50 flex w-full h-full items-center justify-center">
<div tw="flex flex-col md:flex-row w-full py-12 px-4 md:items-center justify-between p-8">
<h2 tw="flex flex-col text-3xl sm:text-4xl font-bold tracking-tight text-gray-900 text-left">
<span>Ready to dive in?</span>
<span tw="text-indigo-600">Start your free trial today.</span>
</h2>
<div tw="mt-8 flex md:mt-0">
<div tw="flex rounded-md shadow">
<a href="#" tw="flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-5 py-3 text-base font-medium text-white">Get started</a>
</div>
<div tw="ml-3 flex rounded-md shadow">
<a href="#" tw="flex items-center justify-center rounded-md border border-transparent bg-white px-5 py-3 text-base font-medium text-indigo-600">Learn more</a>
</div>
</div>
</div>
</div>
`;
const fontFile = await fetch('https://og-playground.vercel.app/inter-latin-ext-400-normal.woff');
const fontData: ArrayBuffer = await fontFile.arrayBuffer();
export const GET: RequestHandler = async () => {
return new ImageResponse(template, {
height: 250,
width: 500,
fonts: [
{
name: 'Inter Latin',
data: fontData400,
weight: 400
}
]
});
};
As you know, satori supports tailwind css too this example shows html template with tailwind css.
In above code snippet, We are importing import { ImageResponse } from '@ethercorps/sveltekit-og';
and then we defined our html template and we passed it to ImageResponse
with options and for more read readme.
- Example:
ComponentToImageResponse
Here, I'm going to use Geoff Rich's Component Code.
+server.ts
import Card from '$lib/Card.svelte';
export const GET: RequestHandler = async () => {
return new ComponentToImageResponse(Card, {}, {
height: 250,
width: 500,
fonts: [
{
name: 'Inter Latin',
data: fontData400,
weight: 400
}
]
});
};
Here, I'm passing Card
component and a empty dict {}
which going to contain props for our component with options and this will going to return Image Response for your OG meta tag.
This is all you going to need for generating dynamic OG Images for your shareable links.
This is me writing for you. If you wanna ask or suggest anything please put it in comment and show some love ❤️.
In India, after few days we have a festival
Deewali
orDeepawali
. I wish you all a very happy diwali 🪔.
Posted on October 20, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.