DEV Community

Cover image for Announcing SvelteKit OG v4: An alternative to @vercel/og for sveltekit
Shivam Meena
Shivam Meena

Posted on

Announcing SvelteKit OG v4: An alternative to @vercel/og for sveltekit

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-og like 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 components too.
// + 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
    });
};
Enter fullscreen mode Exit fullscreen mode

Preview:
Raw html preview

  • 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>
Enter fullscreen mode Exit fullscreen mode

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.' }
    );
};
Enter fullscreen mode Exit fullscreen mode

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 }),
];
Enter fullscreen mode Exit fullscreen mode

Preview:
Raw html preview

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)