SvelteKit OG 4.4 renders OG images on the main thread or in a web worker
SvelteKit OG 4.4 adds a client entry that renders OG images in the browser or a web worker, with no server request. A dev.to post says it targets live editor previews and adapter-static sites that have no server.
The Engineer · Build desk
Worker rendering needs worker format es in Vite config or vite build fails. Components keep only inline styles. Satori raster formats other than png render as png; Takumi outputs jpeg and webp. Satori-only projects must install takumi-js. Only Vite is supported as bundler.
- constraint Worker rendering setups Needs worker format "es" in Vite config; without it vite dev works but vite build fails, claim 14
- constraint Components with scoped styles Only inline styles are captured; scoped style blocks and css="injected" do not come through, claim 13
- constraint Satori jpeg or webp users Satori raster formats other than png render as png; Takumi outputs jpeg and webp, claim 10
- cost Satori-only projects takumi-js must still be installed, because Vite resolves the Takumi chunk at build time, claim 6
- constraint Projects on other bundlers Client entry loads wasm and fonts via Vite ?url imports; other bundlers are not supported, claim 7
| Who | How | Kind | Claim |
|---|---|---|---|
| Worker rendering setups | Needs worker format "es" in Vite config; without it vite dev works but vite build fails | constraint | 14 |
| Components with scoped styles | Only inline styles are captured; scoped style blocks and css="injected" do not come through | constraint | 13 |
| Satori jpeg or webp users | Satori raster formats other than png render as png; Takumi outputs jpeg and webp | constraint | 10 |
| Satori-only projects | takumi-js must still be installed, because Vite resolves the Takumi chunk at build time | cost | 6 |
| Projects on other bundlers | Client entry loads wasm and fonts via Vite ?url imports; other bundlers are not supported | constraint | 7 |
What happened
- Before 4.4, every OG image the package produced came from a server route.
- The client entry runs the same two engines as the server: Takumi by default, or Satori with ReSVG.
- Worker rendering needs worker.format set to es in the Vite config, since the default iife format cannot code-split the per-engine chunks.
- Only inline styles survive when a component is rendered; scoped style blocks and css="injected" do not come through.
Why it matters
- cost The worker setting fails late. Without it vite dev works and vite build does not, so the first error appears at build time.
- constraint A card component written with scoped style blocks loses those styles in the image and has to move to inline styles or the stylesheets and Tailwind options.
- decision Teams that want webp or jpeg previews need Takumi, the default engine, because Satori raster output other than png comes back as png.
The entry point is one function. `createImage` takes an HTML string or a Svelte component and returns a standard Response, read with `.blob()`, `.arrayBuffer()` or `.text()` [9]. For a component, the package mounts it into a detached shadow root, captures the HTML and unmounts it, so page CSS cannot leak into the image [12].
The post's example ends at an object URL: it turns the blob into one with `URL.createObjectURL` and sets it as an `<img>` source [17]. In our view, that URL is local to the tab that made it, so a link-preview crawler requesting an og:image address cannot reach it. The post does not show how a browser-rendered image gets to an address a crawler can fetch. Until it does, the adapter-static claim covers rendering and leaves delivery to the team.
The client entry loads its WebAssembly and fonts through Vite `?url` imports, and the post says other bundlers are unsupported [7]. The call has to stay out of server rendering. Set `export const ssr = false;` in the route's `+page.ts`, or call the API only inside `onMount` or an event handler [8].
Install weight and visitor weight differ. `takumi-js` has to be installed even for Satori-only use, because Vite resolves the Takumi chunk at build time [6]. It adds a dependency for the engine you did not pick, though the install is a single command [6]. At runtime each engine is its own chunk behind a dynamic `import()`, so a Takumi user never fetches Satori's WebAssembly, and the reverse holds [11].
The worker path is where the post claims a performance benefit. Rendering in a module worker keeps the main thread free while the wasm runs [16]. The post's handler reads the result with `.arrayBuffer()` and posts it back with the buffer in the transfer list [15], so the page receives the buffer itself.
On compatibility the post is direct: "The server API doesn't change; this release is purely additive." [5] That is the post's own claim. If it holds, a team with working server routes has nothing to migrate. The post also links a playground that renders in the browser and shows the matching `createImage(...)` call, so output can be checked before installing anything [18].
What to watch
- Whether the package documents a way to get a browser-rendered image to a URL a link-preview crawler can fetch, which adapter-static sites would need.
- Reports of vite build failures from teams that leave the worker format at its default iife.
- Side-by-side checks of playground output against the server route for the same HTML, since the post says both produce the same images.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence50
- Adoption
- Insufficient
- Hype gap−5
- Incentives
- Insufficient
- Confidence55
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
SvelteKit OG 4.4 adds an @ethercorps/sveltekit-og/client entry that renders the same images on the main thread or in a web worker, with no server request.
- [2]
Until 4.4, every OG image came from a server route.
- [3]
The post names two uses: live previews, where an editor or CMS shows the OG card as someone types without calling an endpoint on every keystroke, and static sites, where an adapter-static site has no server but can still generate images at runtime.
- [4]
The client entry uses the same two engines as the server: Takumi (the default) or Satori + ReSVG.
- [5]
The post states: "The server API doesn't change; this release is purely additive."
- [6]
takumi-js must be installed alongside @ethercorps/sveltekit-og with a single npm i command, even for Satori-only use, because Vite resolves the Takumi chunk at build time.
- [7]
The client entry loads its WebAssembly and fonts through Vite ?url imports; other bundlers are not supported.
- [8]
The client API is browser-only: set export const ssr = false; in the route's +page.ts, or call the API only inside onMount or an event handler.
- [9]
createImage returns a standard Response, read with .blob(), .arrayBuffer() or .text(); ImageResponse is the same thing as a Response subclass.
- [10]
Satori outputs png or svg and Takumi outputs png, jpeg, webp, ico, raw or svg; Satori raster formats other than png render as png.
- [11]
Only the engine in use is downloaded: each engine is its own chunk behind a dynamic import(), so Takumi users never fetch Satori's WebAssembly and the other way round.
- [12]
When given a component, the package mounts it into a detached shadow root, captures the HTML, then unmounts it; the shadow root never joins the page, so page CSS cannot leak into the image.
- [13]
Only inline styles are captured from a component; scoped style blocks and css="injected" do not come through, so use inline styles or the stylesheets / Tailwind options.
- [14]
Worker rendering requires worker: { format: "es" } in the Vite config, because the client entry splits into one chunk per engine and the default iife format cannot code-split; without it vite dev works but vite build fails.
- [15]
The post's worker example reads the result with .arrayBuffer() and posts it back with the buffer in the transfer list.
- [16]
Rendering inside a module worker keeps the main thread free while the wasm runs.
- [17]
The post's first example reads the response with .blob(), turns it into an object URL with URL.createObjectURL, and sets it as an img source.
- [18]
A playground that renders in the browser with this API and shows the matching createImage(...) call lets readers try it before installing anything.
Get this ledger inside ChatGPT. Ask about any of our stories and the answer brings its figure, each claim’s verdict and the sources. Free, no account.
Sources
1 independent publisher whose own reporting we read for this story.
- dev.toSvelteKit OG 4.4: Generate OG Images in the Browser
1 article · October 14, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.