Skip to content

Carve vs Markdown, Djot & MDX

How Carve compares to the markup languages you already know. The short version: Markdown's reach, Djot's consistency, web-native features by default - without turning your content into a JavaScript program.

This page is Carve-centric: a feature-by-feature take against Markdown, Djot, and MDX. For a broader, neutral survey of the wider lightweight-markup landscape (AsciiDoc, reStructuredText, Textile, and more), see Modern Markup Languages Comparison.

Legend

✅ native  ·  🧩 plugin / extension needed  ·  ⚠️ partial / convention  ·  ❌ not available

At a glance

Markdown (CommonMark)DjotMDXCarve
Formal, normative grammar⚠️ spec, ambiguous edges❌ (md + JSX)✅ EBNF
Consistent inline rules
No-backtracking parse guarantee✅ *
Markdown-familiar syntax⚠️⚠️
Paragraph interruption (no blank line)✅ **
Feature completeness/consistency

* Inline parsing is single-pass with a delimiter stack; at the block level a fence / ::: opener uses a bounded forward scan for a matching closer (closer lookahead, not backtracking) - see Technical Rationale. ** Like Markdown for quotes, headings, tables and closed fences; list markers (both bullet and ordered) deliberately never interrupt a paragraph - a list needs a blank line (no CommonMark 1.-only heuristic).

Paragraph interruption, by rule count

How many distinct rules an author has to remember for "when does a block break an open paragraph without a blank line". Fewer and more regular is easier to learn and harder to get wrong. (Counts are author-facing rules, not formal grammar productions, so they are approximate - the point is the regularity.)

ModelInterruption rules
Markdown (CommonMark)~8–10, irregular - setext underline, ordered list only if it starts with 1, indented code can't interrupt, HTML-block type 7 can't, bullet only if the first item is non-empty, …
MDXinherits Markdown's (~8–10), plus JSX block handling
Djot1 - nothing interrupts; a blank line precedes every block
Carve3 - visible block-openers interrupt (heading, quote, table row, open fence, thematic break); list markers fold (never interrupt); fence / ::: closers and bare images don't interrupt

Carve trades Djot's single uniform rule for Markdown familiarity on the common blocks, but keeps it to three regular rules instead of CommonMark's pile of special cases.

Authoring features

MarkdownDjotMDXCarve
Tables🧩 GFM🧩 GFM
Table rowspan / colspan
Footnotes🧩🧩
Math🧩🧩
Definition lists🧩🧩
Admonitions / callouts🧩⚠️ via div🧩 component✅ native
Attributes {.class #id}🧩 (Pandoc, markdown-it-attrs)⚠️ JSX props
Generic divs / spans🧩 (Pandoc fenced divs / spans)⚠️ components
Smart typography🧩🧩
Editorial / critic markup
Frontmatter⚠️ tooling🧩
Symbols / emoji shortcodes🧩✅ symbols, mapped via filters🧩:name: symbols, mapped via config

Docs & cross-referencing

MarkdownDjotMDXCarve
Automatic heading ids⚠️ tooling🧩✅ case-preserving, case-insensitive refs
Cross-references </#id>
Implicit heading refs [Heading][]🧩 (Obsidian uses [[…]])

Safety & ecosystem

MarkdownDjotMDXCarve
Sanitization mandated by the spec❌ consumer's job❌ consumer's jobn/a✅ §25, corpus-pinned
URL / attribute / DoS hardening by default✅ always on
Raw HTML default⚠️ on (libs vary)⚠️ onruns JS⚠️ on, one-flag opt-out / safe mode
Embeds live components / JS✅ (its purpose)❌ by design
Independent implementationsmany, divergentseveral (js, lua, rust, go)JS-onlyphp · js · rs, conformance-tested
Shared spec test corpus⚠️

See Security → How Carve compares on security for the detailed breakdown and caveats.

When to pick which

  • Markdown - you need maximum reach and only basic formatting; ambiguity and plugin-juggling are acceptable.
  • Djot - you want Markdown's spirit with a clean, consistent grammar, and a single implementation is fine.
  • MDX - your docs are an app; you want to embed live React/JS components and accept that content runs code.
  • Carve - you write cross-referenced, content-rich docs (handbooks, specs, knowledge bases) and want batteries-included syntax, predictable output, and the same result across php / js / rs - without a build step that executes JavaScript.

Want the full reasoning?

See Technical Rationale for the parser contract and Divergence from Djot for the specific design calls (case-preserving heading ids, content-required list markers, the + continuation marker).

Released under the MIT License.