Getting Started
This guide walks you through creating a simple content-managed app with Verevoir in under five minutes. By the end you'll have a content model, an in-memory database, and a React editor — all running locally.
Install
npm install @verevoir/schema @verevoir/storage @verevoir/editor
Peer dependencies: react, react-dom, zod.
Define a Content Model
A content model describes the shape of your content. Define it once — Verevoir generates validation, TypeScript types, and editor metadata from the same definition.
import { defineBlock, text, richText, boolean } from '@verevoir/schema';
export const article = defineBlock({
name: 'article',
fields: {
title: text('Title').max(120),
body: richText('Body'),
published: boolean('Published').default(false),
},
});
See defining-content-models.md for the full field type reference.
Connect Storage
Verevoir is database-agnostic. For development, use the built-in MemoryAdapter. For production, swap in PostgresAdapter or build your own.
import { MemoryAdapter } from '@verevoir/storage';
const db = new MemoryAdapter();
await db.connect();
Create and retrieve documents:
const doc = await db.create('article', {
title: 'Hello World',
body: 'My first article.',
published: false,
});
const fetched = await db.get(doc.id);
Add the Editor
The BlockEditor component renders a form from your content model definition. No configuration needed — it reads field metadata (labels, types, validation) directly from the block.
import { BlockEditor, useBlockForm } from '@verevoir/editor';
import { article } from './content-model';
function ArticleEditor({ value, onChange }) {
const [state, actions] = useBlockForm(article, value, onChange);
return <BlockEditor block={article} state={state} actions={actions} />;
}
Wire it to storage:
function App() {
const [value, setValue] = useState({
title: '',
body: '',
published: false,
});
const handleSave = async () => {
article.validate(value); // throws on invalid data
await db.create('article', value);
};
return (
<div>
<ArticleEditor value={value} onChange={setValue} />
<button onClick={handleSave}>Save</button>
</div>
);
}
Validate
Every block has a .validate() method powered by Zod. Call it before saving to enforce your content model rules.
try {
article.validate(value);
} catch (err) {
console.error(err.issues); // Zod validation errors
}
Type-safe data is available via InferBlock:
import type { InferBlock } from '@verevoir/schema';
type Article = InferBlock<typeof article>;
// { title: string; body: string; published: boolean }
Next Steps
You now have schema, storage, and editor working together. From here:
- Integration Guide — publishing, internal linking, AI-assisted editing
- Content Controls — polymorphic blocks for page builders
- Access Control — auth, roles, and workflows
- Media — image and video display with imgproxy
- Building a Storage Adapter — connect your own database