Introduction
We're thrilled to announce the official release of @ethercorps/sveltekit-og@v4! This major version represents a complete architectural overhaul focused on delivering uncompromising stability, simplifying the developer API, and providing the tools needed for robust, high-performance image generation across all Javascript runtimes.
This release introduces powerful new features that eliminate manual steps and complex workarounds.
I learnt a lot while fixing
@ethercorps/sveltekit-oglike wasm issue, runtime issues and developer experience with typescript.
Resources
Core of the v4
With v4, we focused on fixing issues related to runtime and fonts. We are happy with the results and I hope it makes you happy too.
Runtime Stability & Reliability
The core architecture was rebuilt to ensure cross-runtime reliability, resolving subtle issues related to wasm loading. Images now render flawlessly whether you deploy to:
- Cloudflare Workers & Pages (@sveltejs/adapter-cloudflare)
- Vercel (@sveltejs/adapter-vercel) - Preview
- Node.js (@sveltejs/adapter-node) - Clone the example from repo
- Netlify (@sveltejs/adapter-netlify) - Preview
- Deno - Preview
- Bun - Still not supported.
The library now explicitly guides developers toward safer, platform-agnostic patterns, making the setup "rock-solid" out of the box.
Ultimate Font Utilities (v4.2.0 Update)
Loading fonts and managing how to load them remote or locally, It was a pain. The v4.2.0 update introduces multiple fonts utility:
CustomFont: Use this class for local files (read via $app/server/read) or custom remote URLs (read via fetch). Supports lazy loading.
GoogleFont: The easiest way to use remote fonts. It automatically handles fetching the CSS, parsing the .ttf URL, and applying in-memory caching for maximum performance.
resolveFonts: A crucial utility function that transforms your array of font class instances into the final, resolved ArrayBuffer objects required by the underlying Satori engine.
How to use
To get started with @ethercorps/sveltekit-og, checkout -> setup in docs
- Basic Example: Here, we will use raw html but we can also use
svelte componentstoo.
// + src/routes/og/server.ts
import { ImageResponse } from '@ethercorps/sveltekit-og';
import type { RequestHandler } from '@sveltejs/kit';
import JetbrainsRegular from "$lib/fonts/JetBrainsMono-Regular.ttf?url"
import { GoogleFont, CustomFont, resolveFonts } from '@ethercorps/sveltekit-og/fonts';
import { read } from '$app/server';
const template = `
<div class="bg-gray-50 flex flex-col w-full h-full justify-center">
<div class="flex flex-col w-full justify-between p-8">
<h2 class="flex flex-col text-3xl sm:text-4xl font-bold tracking-tight text-gray-900 text-left">
<span>Ready to dive in?</span>
<span class="text-indigo-600">Start your free trial today.</span>
</h2>
<div class="flex flex-row items-start">
<div class="flex rounded-md shadow">
<button class="flex justify-center rounded-md border border-transparent bg-indigo-600 px-5 py-3 text-base font-medium text-white">Get started</button>
</div>
<div class="ml-3 flex rounded-md shadow">
<button class="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</button>
</div>
</div>
</div>
</div>
`;
const jetbrainsMonoBoldURL = 'https://github.com/JetBrains/JetBrainsMono/raw/master/fonts/ttf/JetBrainsMono-Bold.ttf'
const fonts = [
new CustomFont('JetBrains Mono', () => read(JetbrainsRegular).arrayBuffer(), {weight: 400}),
new GoogleFont('JetBrains Mono', { weight: 500 }),
new CustomFont('JetBrains Mono', () => fetch(jetbrainsMonoBoldURL).then((res) => res.arrayBuffer()), { weight: 700 }),
];
export const GET: RequestHandler = async () => {
// RESOLVE: Convert the class instances to ArrayBuffers
const resolvedFonts = await resolveFonts([interRegular, interBold]);
return new ImageResponse(template, {
height: 400,
width: 800,
debug: false,
fonts: resolvedFonts
});
};
- Svelte Component Example:
Svelte component which will be converted to png.
// OG.svelte
<script>
let { text, spanText } = $props();
</script>
<div class="bg-gray-50 flex w-full h-full items-center justify-center">
<div class="flex flex-col w-full py-12 px-4 p-8">
<h2 class="flex flex-col text-3xl sm:text-4xl tracking-tight text-gray-900 text-left">
<span class="font-bold">{text}</span>
<span class="text-indigo-600 font-normal mt-4">{spanText}</span>
</h2>
<div class="mt-8 flex md:mt-0">
<div class="flex rounded-md shadow">
<button
class="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</button
>
</div>
<div class="ml-3 flex rounded-md shadow">
<button
class="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</button
>
</div>
</div>
</div>
</div>
Sveltekit Api endpoint to generate and serve generated image.
// +server.ts
import OG from './OG.svelte';
import type { RequestHandler } from '@sveltejs/kit';
import { ImageResponse } from '@ethercorps/sveltekit-og';
import { resolveFonts } from '@ethercorps/sveltekit-og/fonts';
import { fonts } from '../../lib/utils/helper.js';
export const GET: RequestHandler = async () => {
return new ImageResponse(
OG,
{
debug: false,
height: 600,
width: 1200,
fonts: await resolveFonts(fonts)
},
{ text: 'Ready to dive in?', spanText: 'Start your free trial today.' }
);
};
resolveFonts(fonts) is important, so always do it and then pass it to image options.
Helper used for clean code.
// src/lib/utils/helper.js
import { GoogleFont, CustomFont } from '@ethercorps/sveltekit-og/fonts';
import { read } from '$app/server';
import JetbrainsRegular from "./JetBrainsMono-Regular.ttf?url"
const jetbrainsMonoBoldURL = 'https://github.com/JetBrains/JetBrainsMono/raw/master/fonts/ttf/JetBrainsMono-Bold.ttf'
export const fonts = [
new CustomFont('JetBrains Mono', () => read(JetbrainsRegular).arrayBuffer(), {weight: 400}),
new GoogleFont('JetBrains Mono', { weight: 500 }),
new CustomFont('JetBrains Mono', () => fetch(jetbrainsMonoBoldURL).then((res) => res.arrayBuffer()), { weight: 700 }),
];
Conclusion
We believe this update delivers the most stable and developer-friendly experience for generating OG images in the SvelteKit OG Image generation ecosystem. As always, feel free to report any issues or suggest improvements on our GitHub repository!
Checkout newly released docs at sveltekit-og.dev
This is me writing for you after a long time. If you wanna ask or suggest anything please put it in comment and show some love ❤️.
Top comments (0)