:::contentbit

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:

source
:::comparison{left="Basic" right="Pro"}
- Price | Free | $12/mo
- Support | Community | Priority
:::
rendered
BasicPro
PriceFree$12/mo
SupportCommunityPriority

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/blocks

Add a renderer for your target:

pnpm add @contentbit/html   # static HTML strings
pnpm add @contentbit/react  # React components

Parse, 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 | right

React

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-pack

Components land in your app as editable source files. You own them after install.

Next steps

On this page