How to Add Dynamic OG Images to SvelteKit with OGPeek API
SvelteKit gives you file-based routing, server-side rendering, and static prerendering out of the box. But generating unique Open Graph images for every page still requires either manual work or complex server-side tooling. Here's how to solve it in under five minutes with a single API URL—no npm packages, no image libraries, no headless browsers.
Why SvelteKit Needs Dynamic OG Images
When someone shares your SvelteKit app's URL on Twitter/X, LinkedIn, Slack, or Discord, the platform's crawler fetches the page, reads the og:image meta tag, and requests that image to display as a preview card. If every page on your site points to the same generic image—or has no og:image at all—your links look identical in every feed and get fewer clicks.
This matters because social preview cards are the first impression of your content. A blog post titled "Building a Real-Time Dashboard with SvelteKit" should have a social card that says exactly that—not a generic logo or a blank rectangle.
The traditional approaches to solving this each come with friction:
- Manual creation in Figma — Feasible for 5 pages. Unmanageable at 50. Impossible at 500.
- Server routes with Sharp or Puppeteer — You write a
+server.tsendpoint that spins up a headless browser or processes images with Sharp. This adds heavy dependencies, increases server load, and introduces failure modes you have to monitor. - @vercel/og with Satori — Requires a serverless function on Vercel's edge runtime. You write JSX layouts with a restricted subset of CSS. If you deploy to Cloudflare, Netlify, or your own server, this is not an option.
There is a simpler approach: point your og:image meta tag at an external API that returns a PNG. Your SvelteKit app stays lean. The image generation happens somewhere else entirely.
How OGPeek Works
OGPeek is an OG image generation API. You construct a URL with your title, subtitle, template, and brand color as query parameters. When that URL is requested, OGPeek renders the image and returns a 1200×630 PNG—the standard size for Open Graph images across all major platforms.
GET https://ogpeek.com/api/v1/og
?title=Your+Page+Title
&subtitle=yourdomain.com
&template=gradient
&theme=midnight
&brandColor=%23FF7A00
That is the entire integration. A GET request returns a PNG. No API keys for the free tier, no SDK to install, no build configuration to change. You embed this URL in your SvelteKit page's <svelte:head> and every page gets a unique social card based on its title.
Key point: Social platform crawlers follow the URL in your og:image meta tag and fetch whatever is there. They cannot distinguish between a static PNG on your CDN and an API endpoint that generates a PNG on the fly. Both are just HTTP responses with Content-Type: image/png.
Step 1: Add OGPeek to Your Root Layout
SvelteKit uses +layout.svelte files for shared UI that wraps child routes. The root layout at src/routes/+layout.svelte is the ideal place to add default OG meta tags that apply across your entire site.
First, create a +layout.ts (or +layout.server.ts) to expose page metadata:
// src/routes/+layout.ts
export function load() {
return {
siteName: 'Your Site Name',
siteUrl: 'https://yourdomain.com'
};
}
Then use <svelte:head> in your root layout to set the default OG tags:
<!-- src/routes/+layout.svelte -->
<script>
let { data, children } = $props();
</script>
<svelte:head>
<meta property="og:site_name" content={data.siteName} />
<meta property="og:type" content="website" />
</svelte:head>
{@render children()}
This sets site-wide defaults. Individual pages will override these with their own <svelte:head> blocks, including the dynamic OG image URL.
Step 2: Dynamic OG Images on Individual Pages
Each +page.svelte file in SvelteKit can include its own <svelte:head> block. Tags defined in a page's head block take precedence over those in parent layouts. This is where you add the OGPeek URL with the page-specific title.
<!-- src/routes/about/+page.svelte -->
<script>
const title = 'About Us';
const description = 'Learn about our team and mission.';
const ogImage = `https://ogpeek.com/api/v1/og?title=${
encodeURIComponent(title)
}&subtitle=${
encodeURIComponent('yourdomain.com')
}&template=gradient&theme=midnight&brandColor=%23FF7A00`;
</script>
<svelte:head>
<title>{title}</title>
<meta name="description" content={description} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={ogImage} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:image" content={ogImage} />
</svelte:head>
<h1>{title}</h1>
<p>{description}</p>
That is a complete integration for a single page. The og:image meta tag points to OGPeek, which generates a 1200×630 PNG with "About Us" as the title text. Zero dependencies added to your project.
Step 3: Dynamic Routes with Load Functions
The real power shows up with SvelteKit's dynamic routes. If you have a blog at /blog/[slug], each post should get its own unique OG image. The title comes from your load function—whether that loads from a CMS, a database, Markdown files, or an API.
Here is a typical setup with a load function returning post data:
// src/routes/blog/[slug]/+page.ts
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ params }) => {
// Fetch from your CMS, database, or local files
const post = await getPost(params.slug);
return {
title: post.title,
description: post.excerpt,
slug: params.slug
};
};
Then in the corresponding +page.svelte, use the data to construct the OGPeek URL:
<!-- src/routes/blog/[slug]/+page.svelte -->
<script>
let { data } = $props();
const ogImage = `https://ogpeek.com/api/v1/og?title=${
encodeURIComponent(data.title)
}&subtitle=${
encodeURIComponent('Blog · yourdomain.com')
}&template=gradient&theme=midnight&brandColor=%23FF7A00`;
</script>
<svelte:head>
<title>{data.title} — Your Site</title>
<meta name="description" content={data.description} />
<meta property="og:title" content={data.title} />
<meta property="og:description" content={data.description} />
<meta property="og:type" content="article" />
<meta property="og:image" content={ogImage} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={data.title} />
<meta name="twitter:image" content={ogImage} />
</svelte:head>
<article>
<h1>{data.title}</h1>
<p>{data.description}</p>
<!-- Rest of your blog post -->
</article>
Every blog post now gets a branded social card with its own title. A post called "Building Real-Time Features in SvelteKit" produces a social card with that exact text. No manual image creation, no server-side rendering of images.
Reusable SEO Component
If you have many page types, extract the OG logic into a reusable Svelte component to keep things DRY:
<!-- src/lib/components/SEO.svelte -->
<script>
let { title, description, subtitle = 'yourdomain.com' } = $props();
const ogImage = `https://ogpeek.com/api/v1/og?title=${
encodeURIComponent(title)
}&subtitle=${
encodeURIComponent(subtitle)
}&template=gradient&theme=midnight&brandColor=%23FF7A00`;
</script>
<svelte:head>
<title>{title}</title>
<meta name="description" content={description} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={ogImage} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:image" content={ogImage} />
</svelte:head>
Then use it in any page with a single line:
<!-- src/routes/pricing/+page.svelte -->
<script>
import SEO from '$lib/components/SEO.svelte';
</script>
<SEO title="Pricing" description="Simple, transparent pricing." />
<h1>Pricing</h1>
Every page that imports SEO.svelte gets a unique, dynamic OG image without repeating the meta tag boilerplate.
Static vs. Server-Rendered SvelteKit Sites
SvelteKit supports multiple adapters for different deployment targets. OGPeek works with all of them because the integration is just a meta tag in your HTML—nothing runs on your server to generate images.
| Adapter | How It Works | OGPeek Compatible |
|---|---|---|
adapter-static |
Prerenders all pages to static HTML at build time | Yes |
adapter-node |
Runs a Node.js server with SSR | Yes |
adapter-vercel |
Deploys to Vercel with edge/serverless functions | Yes |
adapter-cloudflare |
Runs on Cloudflare Workers | Yes |
adapter-netlify |
Deploys to Netlify with serverless functions | Yes |
With adapter-static, the OGPeek URL is baked into the prerendered HTML. With SSR adapters, the URL is rendered server-side on each request. Either way, social platform crawlers receive valid HTML with a working og:image URL.
Tip: If you use adapter-static with prerender = true, make sure your load functions do not depend on request-time data for OG metadata. The title and description should be deterministic at build time so the correct OGPeek URL gets prerendered into each page.
OGPeek vs. SvelteKit Server Routes vs. Manual Creation
Here is how the three main approaches compare for SvelteKit projects:
| Feature | OGPeek API | SvelteKit +server.ts | Manual (Figma) |
|---|---|---|---|
| Setup time | 2 minutes | 1–3 hours | 5–10 min per image |
| Dependencies added | 0 | Sharp, Puppeteer, or Satori | 0 |
| Server load | Zero (external API) | CPU-intensive per request | Zero |
| Works with adapter-static | Yes | No (needs server) | Yes |
| Hosting lock-in | None | Needs SSR-capable host | None |
| Scales to 1000+ pages | Yes | Depends on server capacity | No |
| Custom CSS layout | Pre-built templates | Full control | Full control |
| Maintenance burden | None | Ongoing (deps, runtime) | Per-page manual effort |
For most SvelteKit projects—blogs, SaaS landing pages, documentation sites, portfolios—OGPeek's pre-built templates handle the common cases without adding complexity to your deployment pipeline.
Customizing Your OG Images
OGPeek supports several parameters to match your brand:
- template —
basic,gradient,split, orhero. The gradient template works well for most developer sites. - theme —
dark,light,midnight,forest,sunset. Choose one that matches your SvelteKit app's palette. - brandColor — Any hex color, URL-encoded (e.g.,
%23FF4500for Svelte's orange). Used as an accent in the generated image. - subtitle — Your domain, tagline, or section name. Appears below the title for additional context.
You can make any parameter dynamic. For example, pass the blog category or section name as the subtitle:
const ogImage = `https://ogpeek.com/api/v1/og?title=${
encodeURIComponent(data.title)
}&subtitle=${
encodeURIComponent(`${data.category} · yourdomain.com`)
}&template=gradient&theme=midnight&brandColor=%23FF4500`;
Testing Your OG Images
After deploying your SvelteKit site, verify your OG images work correctly:
- View the page source and confirm the
og:imagemeta tag contains a valid OGPeek URL with your page title encoded in it. - Open the OGPeek URL directly in your browser. You should see a 1200×630 PNG with your title rendered on it.
- Use a debugger tool like the Twitter Card Validator or Facebook Sharing Debugger to see exactly what the platform will display.
- Share a link in a private Slack channel or Discord server to see the preview card in a real context.
If the image does not appear, check that encodeURIComponent() is encoding special characters correctly. Ampersands, quotes, and hash symbols in titles can break the URL if left unencoded.
Performance Considerations
OGPeek caches generated images at the CDN edge. The first request for a given URL renders the image and caches it. Subsequent requests—including from different social platforms sharing the same link—are served from the cache with sub-50ms response times.
Since the og:image URL is only fetched by social platform crawlers (not by your site's actual visitors in their browsers), there is zero impact on your SvelteKit app's performance metrics. Core Web Vitals, Lighthouse scores, and page load times are completely unaffected.
Frequently Asked Questions
Does OGPeek work with SvelteKit's static adapter?
Yes. OGPeek works perfectly with @sveltejs/adapter-static. The og:image meta tag contains a URL to OGPeek's API, so the image is generated externally when a social platform requests it. Your SvelteKit site can be fully static and prerendered.
Do I need to install any npm packages to use OGPeek with SvelteKit?
No. OGPeek requires zero npm packages. You add a URL to your <svelte:head> block and pass your page title as a query parameter. There is nothing to install, configure, or maintain in your project dependencies.
Can I generate different OG images for each dynamic route in SvelteKit?
Yes. In your +page.svelte file, use the data returned by your load function to construct a unique OGPeek URL for each route. Pass the page title and any other dynamic text into the URL query parameters using encodeURIComponent(). Each route will get its own branded social card.
How is OGPeek different from generating OG images with SvelteKit server routes?
Generating OG images in SvelteKit server routes (+server.ts) requires running a headless browser or image library like Sharp on your server, which adds dependencies, server load, and complexity. OGPeek offloads all image rendering to an external API. You make a GET request and get a PNG back. No server-side image processing on your infrastructure.
What image size does OGPeek return for social cards?
OGPeek returns 1200×630 PNG images by default, which is the recommended size for Open Graph images across Facebook, LinkedIn, Twitter/X, Slack, Discord, and other platforms that render link previews.
Start generating OG images for your SvelteKit app
Read the full API documentation, explore templates, and grab your integration URL. Free tier available—no signup required.
Get started free → Read the API docsConclusion
SvelteKit's flexibility—static prerendering, server-side rendering, edge deployment—makes it one of the most versatile frameworks for building modern web applications. Adding dynamic OG images should not compromise that flexibility or add complexity to your deployment.
With OGPeek, you add a URL to your <svelte:head> block, pass the page title as a parameter, and every page on your site gets a unique, branded social preview card. Extract it into a reusable SEO.svelte component and the integration is a single line per page.
No npm packages. No headless browsers. No server-side image processing. No hosting lock-in. Just a GET request that returns a PNG.
Your SvelteKit app stays lean. Your OG images stay dynamic.