Skip to content

Markdown body editing

CaretCMS can edit prose from Astro content collections without moving that content into a separate database. An editor clicks the rendered paragraph or heading, makes a change on the page, and publishes it back to the original .md file.

Use the Markdown storage adapter. CaretCMS selects it automatically when it finds collections under src/content/, or you can set it yourself:

astro.config.mjs
import { defineConfig } from 'astro/config';
import caret, { markdownStorage } from '@caretcms/core';
export default defineConfig({
integrations: [
caret({
storage: markdownStorage({ contentRoot: './src/content' }),
}),
],
});

Markdown body editing is enabled by default. Set bodyEditing: false in caret() if you only want frontmatter fields to be editable.

Render the entry with Astro’s normal content collection API:

src/pages/blog/[...slug].astro
---
import { getEntry, render } from 'astro:content';
const post = await getEntry('blog', Astro.params.slug);
if (!post) return Astro.redirect('/404');
const { Content } = await render(post);
---
<article class="post-body">
<Content />
</article>

CaretCMS adds its editing markers while Astro renders the Markdown. You do not add data-caret to each prose block.

  1. Sign in and click a marked paragraph, heading, list item, or supported paragraph inside a blockquote.
  2. Edit the text or use the inline formatting toolbar.
  3. Leave the field to save it as a private draft. The .md file has not changed yet.
  4. Use Publish to write the draft to the source file.
  5. Commit and deploy the source change, or use CaretCMS commit-on-publish and a rebuild webhook.

For a statically built page, visitors see the published change after the next build. A server-rendered route that reads the source at request time can show it on the next request.

Source block Editable
Paragraph Yes
# ATX heading Yes
First paragraph in a list item Yes, when its source fits on one line
Paragraph inside a blockquote Yes, when its source fits on one line
Setext heading No
Table or code block No
Raw HTML block No
MDX prose, JSX, or a component island No in 0.3.0

Nested blocks stay read-only when their source spans more than one line. This prevents an edit from removing list indentation or blockquote markers.

The toolbar can create:

  • bold and emphasis;
  • links.

Existing inline code round-trips safely, but 0.3 does not provide an inline-code toolbar button. Shift+Enter can create a line break only in a top-level paragraph; headings and nested list/quote blocks reject it so their source structure remains intact.

CaretCMS escapes Markdown punctuation in normal text. It rejects markup outside this small set instead of guessing how to convert it.

Editing a link rewrites it as an inline Markdown link. Link titles are not kept, and a reference-style link becomes an inline link with the same text and URL.

Each prose draft stores the source range and a hash of the text that was edited. CaretCMS checks that range when saving the block and checks every drafted range again before Publish. If the source changed before the block save, the editor reverts that field and asks for a reload so Astro can generate fresh stamps. If it changed after a draft was saved, Publish returns a conflict, leaves the file and draft untouched, and lets the editor reload and reapply the intent.

When an entry contains several edited blocks, CaretCMS checks all of them before writing. One stale block stops the whole entry from publishing.

CaretCMS uses Astro 7’s Markdown processor when it is active and falls back to a Remark plugin for Astro 6 or another unified-based processor. Both paths produce the same editing markers and use the same source checks when publishing.

The release test suite covers both Astro versions, accented characters, emoji, Windows line endings, files without a final newline, concurrent edits, stale source ranges, history restoration, and exact preservation of unedited text.