OpinlySDK

SvelteKit

Render your Opinly blog in SvelteKit — server load functions, Svelte rendering, and SEO via the OpinlySeo component.

In SvelteKit you fetch content in a server load function, render it with @opinly/svelte, and add SEO with @opinly/sveltekit's <OpinlySeo> component (which writes into <svelte:head>).

Install

pnpm add @opinly/backend @opinly/svelte @opinly/sveltekit @opinly/shared
npm i @opinly/backend @opinly/svelte @opinly/sveltekit @opinly/shared
yarn add @opinly/backend @opinly/svelte @opinly/sveltekit @opinly/shared

Set OPINLY_API_KEY in your environment. Fetching in +page.server.ts keeps it server-only.

Fetch in a server load

Posts are flat, so a post lives at a single-segment route ([slug]). The index, category archives, and authors get their own routes (/blog/+page.server.ts, /blog/category/[slug], /blog/authors/[...]) calling posts()/categories()/ author()params.slug here is always one segment.

// src/routes/blog/[slug]/+page.server.ts
import { createOpinlyClient } from '@opinly/backend'
import type { PageServerLoad } from './$types'

export const load: PageServerLoad = async ({ params }) => {
  const opinly = createOpinlyClient({ apiKey: process.env.OPINLY_API_KEY })
  const post = await opinly.post(params.slug) // flat, single-segment post slug
  const resolved = post ? { type: 'post' as const, data: post } : { type: 'not-found' as const }
  return { resolved }
}

Migrating from category-nested URLs? Add a [category]/[slug] route whose server load calls redirect(308, ...) to the flat post path, so old links resolve.

Render + SEO

<!-- src/routes/blog/[...slug]/+page.svelte -->
<script lang="ts">
  import { OpinlyContent } from '@opinly/svelte'
  import { OpinlySeo } from '@opinly/sveltekit'
  import type { OpinlyNode } from '@opinly/shared'

  let { data } = $props()

  const config = {
    imagesPrefix: 'https://cdn.opinly.ai/REPLACE-ME-xxxxxxxx', // your 21-char CDN namespace
    siteUrl: 'https://example.com',
    blogPrefix: '/blog',
    siteName: 'Acme',
  }
</script>

<OpinlySeo resolved={data.resolved} {config} />

{#if data.resolved.type === 'post'}
  <article class="prose">
    <h1>{data.resolved.data.title}</h1>
    <OpinlyContent content={data.resolved.data.content as OpinlyNode} {config} />
  </article>
{/if}

Images

Point imagesPrefix straight at the CDN (https://cdn.opinly.ai/<your-namespace>) — images load directly from the absolute URL, with no proxy or build config needed. If you'd rather serve them same-origin (e.g. for caching under your domain), add a platform rewrite — Vercel/Netlify rewrites, or a handle hook in src/hooks.server.ts that proxies /images/* to the CDN — and set imagesPrefix: '/images' to match.

<OpinlySeo resolved config jsonLd?> renders title, meta/OG tags, and any JSON-LD you pass into <svelte:head>. <OpinlyContent> renders the body via renderToHtml injected with {@html}.

Notes

  • Works with Svelte 4 and Svelte 5 (runes).
  • Style the body with your own classes or the classNames prop — see Rendering.