Getting started
Parse, validate, and render your first Content Blocks document.
Content Blocks is a framework-agnostic block layer for Markdown. Authors — humans, CMSes, or LLMs — write Markdown with directive-style blocks. Every example on this site is rendered live by the library it documents:
:::comparison{left="Basic" right="Pro"}
- Price | Free | $12/mo
- Support | Community | Priority
:::| Basic | Pro | |
|---|---|---|
| Price | Free | $12/mo |
| Support | Community | Priority |
The library parses that to a source-mapped AST, validates it against explicit per-block schemas with line-level diagnostics, and renders it through the adapter of your choice: React, static HTML, or a plain-Markdown fallback.
Install
pnpm add @contentbit/core @contentbit/blocksAdd a renderer for your target:
pnpm add @contentbit/html # static HTML strings
pnpm add @contentbit/react # React componentsParse, validate, render
import { createBlockRegistry, parseDocument, validateDocument } from '@contentbit/core'
import { genericBlocks } from '@contentbit/blocks'
import { renderToHtml } from '@contentbit/html'
const registry = createBlockRegistry().use(genericBlocks())
const result = validateDocument(parseDocument(markdown), registry)
if (!result.ok) {
// Each diagnostic has a code, severity, message, and exact source position.
console.error(result.diagnostics)
}
const html = renderToHtml(result.document)Validation always runs before rendering. Invalid content produces diagnostics like:
content.md:12:3 error CB_ROW_COLUMNS
:::comparison rows require 3 columns (label | left | right). Found 2.
hint: Format: - label | left | rightReact
import { ContentBlocks } from '@contentbit/react'
export function Article({ document }: { document: DocumentNode }) {
return <ContentBlocks document={document} renderMarkdown={(md) => <Markdown source={md} />} />
}ContentBlocks ships headless, accessible defaults for every generic block. Pass
components to override any of them — see Renderers.
Plug in your Markdown library (don't skip this)
Content Blocks parses blocks only — prose and block bodies are handed to a
renderMarkdown function you provide. Without it, **bold** and links render as
literal characters. The wiring is one component:
import ReactMarkdown from 'react-markdown' // or marked, markdown-it, remark...
<ContentBlocks
document={result.document}
renderMarkdown={(md) => <ReactMarkdown>{md}</ReactMarkdown>}
/>Step-by-step setups for react-markdown, marked, markdown-it, and unified/remark: Plug in your Markdown library.
Styled components via shadcn
A polished, Tailwind-styled pack is distributed through a shadcn registry. Configure
the namespace in your components.json:
{
"registries": {
"@contentbit": "https://contentbit.dev/r/{name}.json"
}
}Then install:
pnpm dlx shadcn@latest add @contentbit/generic-packComponents land in your app as editable source files. You own them after install.
Next steps
- Plug in your Markdown library
- Learn the block syntax
- Define custom blocks
- Generate LLM authoring instructions from your registry
- Browse the block reference