The practical SvelteKit pattern is a server endpoint that returns an ImageResponse. Install @ethercorps/sveltekit-og v4 for Svelte 5, register its Vite plugin, create a Svelte component (or HTML template), and return that component from src/routes/.../+server.ts. Use request-time rendering for content that changes per request; use prerendered entries when every image variant is known during the build.
What you are building
An Open Graph image is the preview graphic social networks and messaging clients can show for a URL. In SvelteKit, the image itself can be another route, such as /og.png or /docs/[...slug]/og.png. The route receives a request, renders a Svelte component through SvelteKit OG, and sends the resulting PNG (or another supported output) as the response.
SvelteKit OG uses Satori to convert supported HTML and CSS into SVG, then Resvg to rasterize that SVG. It is not a headless-browser screenshot pipeline, so design your template around the CSS features the current SvelteKit OG documentation supports, especially flexbox-oriented layouts. Components and HTML/CSS strings are both supported.
Requirements and version choices
- Svelte 5 or later.
- SvelteKit 4.1.0 or later if you want the preferred
sveltekitOG()Vite plugin. @ethercorps/sveltekit-ogv4. Earlier package versions are unmaintained according to the project guide.- A deployment adapter/runtime that can execute your endpoint and load the renderer and any fonts you use.
For SvelteKit 4.0, the project still documents its Rollup plugin; that path is planned for deprecation in SvelteKit OG v5. After changing Vite configuration, restart the development server.
#1 Best Overall
Install and configure SvelteKit OG
Install the package
npm i @ethercorps/sveltekit-og
Use the equivalent command for pnpm, yarn, or another package manager if your project uses one.
Register the Vite plugin
Add the plugin to vite.config.ts. Keep your existing SvelteKit configuration and insert sveltekitOG() in the plugins array:
import { sveltekit } from '@sveltejs/kit/vite';
import { sveltekitOG } from '@ethercorps/sveltekit-og/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit(), sveltekitOG()]
});
The exact import path should match the v4 package documentation for your installed release. Restart Vite after saving this file.
Create a reusable Open Graph template
Put a component beside the route, for example at src/routes/og/OgTemplate.svelte. The root element should fill the output dimensions. If you use a component-level <style> block, enable CSS injection with <svelte:options css="injected" />.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →<svelte:options css="injected" />
<script lang="ts">
export let title: string;
export let description: string = '';
export let siteName: string = 'Example site';
</script>
<div class="canvas">
<div class="brand">{siteName}</div>
<h1>{title}</h1>
{#if description}
<p>{description}</p>
{/if}
</div>
<style>
.canvas {
width: 1200px;
height: 630px;
display: flex;
flex-direction: column;
justify-content: center;
padding: 72px;
box-sizing: border-box;
background: #111827;
color: white;
font-family: Arial, sans-serif;
}
.brand { font-size: 30px; color: #93c5fd; margin-bottom: 28px; }
h1 { font-size: 68px; line-height: 1.05; margin: 0 0 24px; }
p { font-size: 30px; line-height: 1.25; margin: 0; color: #d1d5db; }
</style>
The documented example uses 1200 by 630 pixels. Treat that as a useful template size, not a universal requirement imposed by every social platform. Keep layouts simple, and verify any advanced CSS, external assets, or font behavior against the current package documentation.
Return an image from a SvelteKit server route
Create src/routes/og/+server.ts:
import { ImageResponse } from '@ethercorps/sveltekit-og';
import OgTemplate from './OgTemplate.svelte';
export const GET = async ({ url }) => {
const title = url.searchParams.get('title') ?? 'SvelteKit Open Graph image';
const description = url.searchParams.get('description') ?? 'Generated at request time';
return new ImageResponse(
OgTemplate,
{
width: 1200,
height: 630,
props: { title, description, siteName: 'Example site' }
}
);
};
Your installed v4 release may expose slightly different constructor typing; follow that release’s API reference if TypeScript reports a signature mismatch. The essential contract is unchanged: a +server.ts GET handler returns an ImageResponse.
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
Use the image in page metadata
In a page’s server load function, construct an absolute URL for the image route and pass it to your Open Graph metadata. A query-string example is:
const image = new URL('/og', url);
image.searchParams.set('title', data.title);
image.searchParams.set('description', data.summary);
return { image: image.href };
Render that value as the page’s og:image (and usually twitter:image) URL in your document head. The image endpoint should be publicly reachable by the crawler requesting the page.
Dynamic route images with parameters
For documentation or blog pages, place an image route next to the parameterized page. For example:
src/routes/docs/[...slug]/og.png/+server.ts
Read params.slug, load the corresponding title, and pass it to the component:
import { ImageResponse } from '@ethercorps/sveltekit-og';
import OgTemplate from './OgTemplate.svelte';
export const GET = async ({ params }) => {
const title = params.slug ? params.slug.replaceAll('-', ' ') : 'Documentation';
return new ImageResponse(OgTemplate, {
width: 1200,
height: 630,
props: { title, description: 'Documentation', siteName: 'Example site' }
});
};
Sanitize or constrain values that come from the URL. Do not place untrusted strings into raw HTML; passing them as component props keeps the template boundary clear.
Choose request-time rendering or prerendering
| Approach | Use it when | Trade-off |
|---|---|---|
| Request-time endpoint | The title, status, locale, or personalization must be resolved when requested. | Rendering work occurs on requests, so the selected runtime must support the renderer, Wasm, dependencies, and fonts. |
| Build-time prerender | All image routes and their content can be enumerated during a build. | Images become static files and avoid per-request rendering, but updates require another build. |
Prerender one fixed image
If the image never changes at runtime, add this export to the route:
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #3
export const prerender = true;
Prerender known parameter values
For parameterized routes, provide an entries generator so SvelteKit knows which variants to build. The project documentation demonstrates an entries function beside a catch-all documentation route. Conceptually:
export const prerender = true;
export async function entries() {
const pages = await getDocumentationPages();
return pages.map((page) => ({ slug: page.slug }));
}
Use the parameter key required by your route (for a catch-all route, that is commonly a slug string or array according to your SvelteKit version). Generated images are saved as static files, reducing runtime work.
Fonts, assets, and CSS limits
- Bundle or load fonts in the manner supported by your adapter and SvelteKit OG version; do not assume browser font loading behaves the same way.
- Prefer local, deterministic assets. A remote image that is unavailable at render time can produce an incomplete image or a failed response.
- Use flexbox-oriented CSS and test each change. Satori supports a subset of HTML/CSS rather than the full browser layout engine.
- Keep the root element exactly the output size, then test long titles, missing descriptions, non-Latin text, and very narrow screens where the image is displayed.
Deployment considerations
Vercel
The SvelteKit OG Vercel guide documents adapter and plugin configuration and warns that a Vercel Edge function’s total bundle must fit under 1 MB, including Wasm, dependencies, and fonts. That is a bundle-size limit, not a rendering-speed measurement. If your renderer and fonts cannot fit, use a runtime/adapter arrangement that permits them or move fixed images to prerendering.
Cloudflare Pages
Cloudflare’s official SvelteKit Pages documentation describes installing and configuring @sveltejs/adapter-cloudflare and implementing request handlers as SvelteKit endpoints. Confirm Wasm and asset behavior for the exact Pages runtime you deploy to; adapters do not all provide identical support.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteTesting checklist
- Run the development server and request a simple image URL such as
/og?title=Hello. - Open the response directly and verify its content type and dimensions.
- Test encoded characters, ampersands, emoji, long titles, and absent query parameters.
- Run a production build with the selected adapter, then request the endpoint from the deployed URL.
- Inspect the rendered page source and confirm that
og:imageis an absolute, publicly reachable URL. - If using prerendering, inspect the generated static output and confirm every expected parameter entry exists.
Troubleshooting common failures
“Cannot find module” or plugin errors
Check that v4 is installed, the import path matches that release, and the Vite server was restarted after editing vite.config.ts. On SvelteKit 4.0, use the documented Rollup path rather than assuming the newer plugin is compatible.
The image is blank or styling is missing
Ensure the component has a full-size root, add <svelte:options css="injected" /> when using component styles, and remove unsupported CSS until a minimal flex layout renders. Verify that fonts and images are available to the deployment runtime.
Rank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
Build fails while prerendering
An entry is missing, a load function is throwing, or the route expects request-only data. Enumerate every slug in entries(), make data loading build-safe, and switch to request-time rendering when values cannot be known at build time.
Works locally but fails after deployment
Compare adapter/runtime capabilities, especially Wasm, font loading, bundle size, and outbound asset access. On Vercel Edge, check the complete 1 MB function limit rather than measuring only your application code.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchSocial networks show an old image
The endpoint may be correct while a crawler is serving a cached result. Change the image URL when you intentionally need cache invalidation, and confirm the crawler can fetch the deployed route without authentication or bot challenges.
Or skip the browser setup
If you need a screenshot-style image of a rendered URL rather than a Svelte component pipeline, ScreenshotNeo provides a single HTTP request. It accepts cookie/consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, with the outcome reported in X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots.
See the ScreenshotNeo API documentation for all options. cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
Recommended Free Tools
Frequently asked questions
Can I return an HTML string instead of a Svelte component?
Yes. SvelteKit OG accepts HTML/CSS templates as well as Svelte components. A component is usually easier to type and reuse when titles and descriptions vary.
Best Value
Is 1200×630 mandatory?
No. It is the dimensions used by the documented example. Choose dimensions appropriate for your consuming platforms and keep the component root and ImageResponse options consistent.
Should every image route be prerendered?
No. Prerender only when routes and data are known at build time. Personalized or frequently changing content belongs in a request-time endpoint.
Does this approach run a browser?
No. Satori produces SVG from supported markup and CSS, and Resvg rasterizes it. That makes it lightweight compared with a headless-browser capture, but it also means browser-only CSS and behavior are unavailable.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Frequently Asked Questions
Can I use the same endpoint for PNG and JPEG output?
Use the output controls documented by the exact SvelteKit OG version you installed, then verify the response content type in your deployed adapter; the core route pattern remains a GET handler returning ImageResponse.
Where should Open Graph metadata be generated?
Generate the absolute image URL in the page’s load/data layer and emit it in the document head as og:image, alongside your other page metadata.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

