@bettercms-ai/sveltekit
v0.4.0
Published
BetterCMS for SvelteKit: settings from PUBLIC_BCMS_*, the read client per request with draft mode, the editor's draft routes, and the page sections server-rendered by BetterCMS.
Readme
@bettercms-ai/sveltekit
BetterCMS for SvelteKit. Your own components stay your own: they spread a few attributes onto their elements, and every element becomes editable in the BetterCMS visual editor. Pages stay current when content is published.
npm i @bettercms-ai/sveltekit// src/lib/bcms.ts
import { env as pub } from "$env/dynamic/public";
import { env as priv } from "$env/dynamic/private";
export const bcms = createBcms({ env: { ...pub, ...priv } });
// src/hooks.server.ts: drafts, <html data-bcms-draft>, <meta name="generator">
export const handle = bcmsHandle(bcms); // or sequence(bcmsHandle(bcms), yourHandle)
// src/routes/api/draft/+server.ts: the path the editor's "Preview on site" opens
export const GET = ({ request }) => bcms.draftEnable(request);
// src/routes/+page.server.ts: drafts while the editor previews
export const load = async ({ request }) => ({ post: await (await bcms.client(request)).getEntry("hello-world") });<!-- src/routes/+layout.svelte: a publish, and the editor's re-render request, re-run `load` -->
<script>
import { onMount } from "svelte";
import { invalidateAll } from "$app/navigation";
import { env } from "$env/dynamic/public";
import { startBcmsLive } from "@bettercms-ai/sveltekit";
onMount(() => startBcmsLive({ refresh: invalidateAll, src: env.PUBLIC_BCMS_LIVE_URL }));
</script>
<slot />
<!-- your own markup, bound to its content -->
<article {...entryAttrs(data.post)}>
<h1 {...entryField(data.post, "title")}>{data.post.data.title}</h1>
<img {...imageAttrs(data.post.data.cover, { sizes: "100vw" })} {...entryField(data.post, "cover")} class="cover" />
<div {...entryField(data.post, "body")}>{@html rich(data.post.data.body)}</div>
</article>| Helper | What it binds |
|---|---|
| fieldAttrs(path, page.fieldStyles) | A page field. It also applies the field's stored style, which turns on the editor's Align, Size and Weight controls. |
| entryAttrs(entry) | A collection entry's card. |
| entryField(entry, path) | A field of that entry. |
| sectionRootAttrs(block) | A section root (its spacing and design). |
| sectionColumnAttrs(block) | The section's content column. |
| imageAttrs(value, { path, sizes }) | An optimized <img> with a srcset, alt text, the focal point and the field binding. |
| rich(value), lines(value), plain(value) | Rich text as HTML, a one-line value, or plain text for attributes. |
Sections
A page built from sections in BetterCMS renders as the hosted renderer draws it, so forms, sliders and tabs work exactly as they do on a BetterCMS-hosted site. Pass the request and the editor's session gets the draft, with every section and value clickable:
// src/routes/+page.server.ts
export const load = async ({ fetch, request }) => ({ sections: await bcms.sections("home", fetch, request) });<div use:bcmsSections={data.sections}>{@html sectionsMarkup(data.sections)}</div>Your own sections in the CMS order
A page that draws its own sections can let BetterCMS order them, and draw sections added in the editor between them: name the page's sections (slots, in source order) and loop over sectionItems. npx @bettercms-ai/convert --sections writes this for you.
// src/routes/+page.server.ts
const bcmsSlots = ["hero", "code:features"];
export const load = async ({ fetch, request }) => ({
bcmsSections: sectionItems(await bcms.sections("home", fetch, request, { slots: bcmsSlots }).catch(() => null), bcmsSlots, "home"),
});{#each data.bcmsSections as item (item.key)}
{#if item.slot === "hero"}<section class="hero" {...item.root}>…</section>
{:else if item.slot === "code:features"}<div {...item.box}><Features /></div>
{:else if item.slot === null}<div data-bcms-rendered="platform" use:bcmsSections={item.html}>{@html item.html}</div>
{/if}
{/each}root goes on a section's own root element (its placement's data-bcms-block and style); box on a wrapper when the section is a component. With no answer, every slot renders in source order.
Settings
Options are read first, then PUBLIC_BCMS_*, then BCMS_*, the same precedence as every adapter:
| Variable | Purpose |
|---|---|
| PUBLIC_BCMS_API_URL | The API to read from. |
| PUBLIC_BCMS_WORKSPACE | Your workspace. |
| PUBLIC_BCMS_PROJECT_ID | Your project. |
| PUBLIC_BCMS_LIVE_URL | The live stream. Optional. |
| BCMS_DRAFT_SECRET | Signs the draft cookie. |
The sections run small inline scripts (forms, sliders and tabs), so a Content-Security-Policy must allow inline scripts.
