Skip to Content
Getting StartedProject setup

Project setup

Blok doesn’t force a layout, but this is the one most teams settle on.

app/ ├─ editor/ │ └─ page.tsx // the <Blok /> mount ├─ preview/ │ └─ [slug]/page.tsx // renders saved Data as a real page ├─ api/ │ └─ pages/route.ts // POST: save, GET: list blok/ ├─ config.ts // Config object (shared with preview) ├─ components/ // block renderers (used by editor AND preview) │ ├─ Hero.tsx │ ├─ Features.tsx │ └─ ... └─ plugins/ └─ my-plugin.ts

Share renderers between editor and preview

The trick most apps want: the same block components should render in the editor and on the real, published page. Put them in their own folder and import from both places.

blok/components/Hero.tsx
export function Hero({ title, subtitle }: { title: string; subtitle: string }) { return ( <section> <h1>{title}</h1> <p>{subtitle}</p> </section> ); }
blok/config.ts
import { Hero } from "./components/Hero"; export const config: Config = { components: { Hero: { label: "Hero", fields: { /* ... */ }, render: Hero, }, }, };

Now your preview page can render the same component without going through the editor:

app/preview/[slug]/page.tsx
import { config } from "@/blok/config"; export default async function Preview({ params }) { const data = await loadPageBySlug(params.slug); return ( <> {data.content.map((block) => { const C = config.components[block.type].render; return <C key={block.props.id} {...block.props} />; })} </> ); }

Save the document

Use the onSave and onPublish callbacks to persist. A typical REST flow:

<Blok config={config} data={initialData} onSave={async (data) => { await fetch(`/api/pages/${pageId}/draft`, { method: "PUT", body: JSON.stringify(data), }); }} onPublish={async (data) => { await fetch(`/api/pages/${pageId}/publish`, { method: "POST", body: JSON.stringify(data), }); }} />

Or use @useblok/sdk’s useBlokDocument hook for a ready-made binding that wires up save/publish/versions/comments/audit/presence to a BlokStorageAdapter. See Real-time bindings.

TypeScript

Blok is TypeScript-first. The most useful types:

import type { Blok, Config, Data, ComponentConfig, Fields, Field, BlockInstance, RootData, } from "@useblok/core";

Fields<Props> and ComponentConfig<Props> let you get prop-level autocomplete in render, defaultProps, and getSummary.

Last updated on