SvelteKit
Use Takumi to render your Svelte components on the server.
Write a component
Set css="injected" so component styles travel with the rendered HTML — Takumi reads CSS from the markup, not from external file imports the browser would resolve.
<script lang="ts">
let { title, description } = $props();
</script>
<svelte:options css="injected" />
<div
id="og-image"
class="w-full h-full flex items-center justify-center p-12 flex-col whitespace-pre-wrap leading-normal"
>
<p class="text-7xl font-semibold text-black">
{title}
</p>
<p class="text-5xl font-medium text-black/75">
{description}
</p>
</div>
<style>
#og-image {
background-image: linear-gradient(to bottom right, var(--color-orange-50), var(--color-red-200));
}
</style>Render it from a +server.ts
render from svelte/server returns { head, body } — concatenate them and pass to ImageResponse. Tailwind (or any other CSS) goes in via the stylesheets option.
import { render } from "svelte/server";
import style from "../../app.css?inline";
import ImageResponse from "takumi-js/response";
import OgImage from "$lib/components/OgImage.svelte";
import type { RequestEvent } from "./$types";
export async function GET({ url }: RequestEvent) {
const { body, head } = await render(OgImage, {
props: {
title: url.searchParams.get("title"),
description: url.searchParams.get("description"),
},
});
return new ImageResponse(`${head}${body}`, {
width: 1200,
height: 630,
stylesheets: [style],
fonts: [
{
name: "Geist",
data: () =>
fetch("https://takumi.kane.tw/fonts/Geist.woff2").then((res) => res.arrayBuffer()),
},
],
});
}Gotchas
Component styles disappear in the rendered image. SvelteKit's default scoping strips <style> blocks during server render. Use <svelte:options css="injected" /> so the styles ship inline with the HTML Takumi reads.
Tailwind classes don't apply. Pass your compiled stylesheet via stylesheets. The ?inline Vite import is the simplest way — it gives you the CSS as a string at build time.
Last updated on