sveltekit-og

repository·main·Indexed 43 days ago

https://github.com/ethercorps/sveltekit-og

A library for dynamically generating Open Graph images in SvelteKit using HTML+CSS or Svelte components without a headless browser. It supports Satori (HTML → SVG → PNG) and Takumi (Rust/WASM) rendering engines, providing ImageResponse and Component Rendering utilities for various deployment targets including Cloudflare Workers, Deno, and Node.js.

Tokens
24.6K
Snippets
92
Records
106
Agent score
54%

What's inside sveltekit-og

  1. What is SvelteKit OG?

    main
    SvelteKit OG is a high-performance utility library for server-side image generation within SvelteKit applications. While primarily used for creating dynamic Open Graph (OG) images for social media previews, it is a generic tool that converts Svelte components or HTML/CSS into high-quality PNGs. It allows you to define a visual template using modern web technologies and render unique images on the fly based on dynamic data or URL parameters.
  2. Key features of SvelteKit OG

    main

    SvelteKit OG provides several developer-centric features:

    • Fast & Lightweight: Runs in edge/serverless environments without a browser instance.
    • Easy Styling: Supports standard CSS (Flexbox) and Tailwind CSS.
    • Svelte Component Support: You can render actual .svelte components as image templates.
    • Typography: Supports custom fonts, Google Fonts, and Emojis.
    • Developer Experience: Fully typed with TypeScript and includes a Debug Mode to inspect layout bounding boxes for design fine-tuning.
  3. How SvelteKit OG achieves Zero-Browser Rendering

    main

    SvelteKit OG avoids the performance overhead of headless browsers (like Puppeteer) by using a two-step rendering pipeline that is compatible with serverless and edge environments (e.g., Vercel Edge, Cloudflare Workers, Netlify functions):

    1. Satori (Vercel): Converts standard HTML and CSS (Flexbox, Tailwind) into an SVG representation.
    2. Resvg: A Rust-based library that converts the SVG into the final PNG or JPEG image file.
  4. How fonts work in sveltekit-og

    main

    To ensure consistent typography in generated images, the rendering engine (Satori) requires font files as raw binary data (ArrayBuffer). You cannot use standard CSS @font-face rules.

    sveltekit-og provides three main ways to manage this:

    1. GoogleFont: Automatically fetches and caches Google Fonts.
    2. CustomFont: Used for local files or remote URLs.
    3. resolveFonts: A utility that converts font instances into the required binary data format.

    Important: The resolveFonts Pattern

    For the standard ImageResponse engine, you must pass your font instances through await resolveFonts([...]) before passing the result to the fonts option.

    Note: If you are using the Takumi engine, you pass font instances directly to fonts and skip resolveFonts.

    import { ImageResponse } from '@ethercorps/sveltekit-og';
    import { GoogleFont, resolveFonts } from '@ethercorps/sveltekit-og/fonts';
    
    const font = new GoogleFont('Inter', { weight: 400 });
    
    export const GET = async () => {
      // Always resolve before passing to ImageResponse
      const resolvedFonts = await resolveFonts([font]);
    
      return new ImageResponse('<div>Hello</div>', {
        fonts: resolvedFonts
      });
    };
  5. Pass dynamic data to Svelte components via props

    main
    When using a Svelte component as the first argument to ImageResponse, you can pass dynamic data (like titles, authors, or dates) using the third argument of the constructor. The types for this object are automatically inferred from the props defined in your Svelte component. This parameter is required if your component expects props.
  6. Choose a rendering engine for Open Graph generation

    main

    The library supports two primary rendering engines for converting HTML/CSS or Svelte components into images:

    1. Satori (Default): Converts HTML → SVG → PNG. It is based on the Satori engine.
    2. Takumi: A Rust/WASM engine available via @ethercorps/sveltekit-og/takumi (v4.3.0+). It is recommended if you need more output formats (such as webp, jpeg, ico, or svg) or want to use its built-in font support.
  7. Configure the legacy Rollup plugin

    main

    If you are using sveltekit-og@v4.0.0, you must use the rollupWasm plugin. This plugin is located inside the rollupOptions block of your vite.config.ts.

    Warning: This method is deprecated and will be removed in v5. Migrate to the Vite plugin as soon as possible.

    import { sveltekit } from '@sveltejs/kit/vite';
    import { rollupWasm } from '@ethercorps/sveltekit-og/plugin';
    import { defineConfig } from 'vite';
    
    const config = defineConfig({
    	plugins: [sveltekit()],
    	build: {
    		rollupOptions: {
    			// Add rollupWasm plugin for Cloudflare compatibility
    			plugins: [rollupWasm()]
    		}
    	}
    });
    
    export default config;
  8. Create a new Svelte project with sv

    main

    Use the sv CLI (powered by sveltejs/cli) to scaffold a new Svelte project. You can either initialize the project in the current directory or specify a new directory name.

    # create a new project in the current directory
    npx sv create
    
    # create a new project in my-app
    npx sv create my-app
  9. Load assets using Public Absolute URLs

    main

    The simplest way to reference assets located in your static folder is to use a full absolute URL. Satori requires an absolute URL (e.g., https://example.com/logo.png) to fetch the image via HTTP.

    Implementation: Construct the URL using the url.origin property from the SvelteKit request object.

    Warning for Edge Runtimes: In Edge Runtimes (Cloudflare, Vercel Edge, Deno), using fetch() to retrieve an asset from your own server via url.origin can cause slow cold starts. In these environments, Vite Inline Import (?inline) or Reading Local Assets ($app/server/read) are strongly preferred.

    import { ImageResponse } from '@ethercorps/sveltekit-og';
    import type { RequestHandler } from './$types';
    
    export const GET: RequestHandler = async ({ url }) => {
    	// Construct the absolute URL using the request URL's origin
    	// Assumes logo.png is in the static folder
    	const absoluteUrl = `${url.origin}/logo.png`;
    
    	const htmlToRender = `
        <div tw="flex">
          <img src="${absoluteUrl}" width="100" height="100" />
        </div>
      `;
    
    	return new ImageResponse(htmlToRender, { width: 1200, height: 630 });
    };
  10. Use local custom fonts with CustomFont

    main

    To use .ttf or .otf files stored within your project, use the CustomFont class. This requires a lazy-loading pattern to ensure the file is read only when needed.

    Implementation Steps

    1. Import the file: Use the Vite ?url suffix (e.g., import MyFont from './font.ttf?url') to get the build-time path.
    2. Wrap in CustomFont: Pass a function to the CustomFont constructor that returns the ArrayBuffer. Use SvelteKit's $app/server read function to retrieve the file.

    Warning: On certain adapters like Netlify, Vite's ?url combined with read may fail. In those cases, use Node's fs module or a public URL.

    import { CustomFont, resolveFonts } from '@ethercorps/sveltekit-og/fonts';
    import { read } from '$app/server';
    import RegularFontPath from '$lib/assets/MyFont-Regular.ttf?url';
    
    const myCustomRegular = new CustomFont(
      'My Custom Font',
      () => read(RegularFontPath).arrayBuffer(),
      { weight: 400 }
    );
  11. Configure the Rollup Plugin for SvelteKit OG

    main

    If you are using sveltekit-og@v4.0.0 or require a Rollup-specific configuration, use the rollupWasm plugin within the build.rollupOptions.plugins array.

    Deprecation Warning: The Rollup plugin will be deprecated in v5 of SvelteKit OG. Migrate to the Vite plugin if possible.

    import { sveltekit } from '@sveltejs/kit/vite';
    import { rollupWasm } from '@ethercorps/sveltekit-og/plugin';
    
    const config = {
    	plugins: [sveltekit()],
    	build: {
    		rollupOptions: {
    			plugins: [rollupWasm()]
    		}
    	}
    };
    
    export default config;