Skip to content

Markdown ​

Markdown is the source of truth in Vizel. The editor renders a Tiptap-backed visual surface, but every save and load round-trips through Markdown so the document remains portable.

Always-on Markdown pipeline ​

The Markdown extension is part of the always-on core: it loads without an opt-in flag. Configure it through the top-level markdown option on VizelEditorOptions.

tsx
import { useVizelEditor, vizelGfmFlavor } from "@vizel/react";

const editor = useVizelEditor({
  markdown: {
    flavor: vizelGfmFlavor,
    encoding: {
      embed: "metadata-comment",
      mention: "metadata-comment",
    },
  },
});
vue
<script setup lang="ts">
import { useVizelEditor, vizelGfmFlavor } from "@vizel/vue";

const editor = useVizelEditor({
  markdown: {
    flavor: vizelGfmFlavor,
    encoding: { embed: "metadata-comment", mention: "metadata-comment" },
  },
});
</script>
svelte
<script lang="ts">
import { createVizelEditor, vizelGfmFlavor } from "@vizel/svelte";

const editor = createVizelEditor({
  markdown: {
    flavor: vizelGfmFlavor,
    encoding: { embed: "metadata-comment", mention: "metadata-comment" },
  },
});
</script>

Flavors ​

A VizelMarkdownFlavor is a plugin object that selects the parser / serializer behavior. Vizel ships five built-ins:

FlavorUse when
vizelCommonMarkFlavorStrict CommonMark output
vizelGfmFlavorGitHub-flavored Markdown (tables, task lists, strikethrough)
vizelObsidianFlavorObsidian-compatible ([[wiki-links]], callouts)
vizelDocusaurusFlavorDocusaurus-compatible (:::admonition)
vizelPandocFlavorPandoc-compatible (footnotes, definition lists)

Compose them with composeVizelMarkdownFlavors([a, b]) — later flavors override earlier ones in nodeSerializers / markSerializers and shallow-merge config.

The pipeline is intentionally asymmetric:

  • Parse tolerantly. Every built-in flavor's markdown-it plugins load on parse, so input remains tolerant across formats.
  • Serialize strictly. Only the selected flavor's serializer hooks run, so output follows that flavor strictly.

Encoding modes for lossy nodes ​

Some Vizel nodes (embed, mention, wikiLink) have no canonical Markdown representation. The markdown.encoding field selects per-node behavior:

Node"default""metadata-comment"
embed[Title](url)[Title](url)<!-- vizel:embed type="..." id="..." -->
mention@username@username <!-- vizel:mention id="..." -->
wikiLinkflavor-dependent ([[page]] for Obsidian; [page](page) elsewhere)flavor-dependent + comment

Pick "default" for portable output that any Markdown reader handles. Pick "metadata-comment" for round-trip fidelity — the trailing HTML comment carries the original node attributes so Vizel can rebuild the exact node on re-import.

When a node has no representation under the selected flavor, the extension emits VizelError("MARKDOWN_LOSSY") (severity warning) through options.onError so consumers can prompt the user before saving.

Round-trip validation for custom flavors ​

When you author a custom flavor, exercise it through assertMarkdownRoundtrip(flavor, samples) from your test runner. The helper parses each sample, re-serializes it, and throws VizelError("MARKDOWN_LOSSY") when the output differs from the input after whitespace normalization.

Reading and writing Markdown at runtime ​

The Tiptap editor exposes getMarkdown() and a setContent() command that accepts a Markdown string when the active flavor recognizes it.

ts
// Read
const md = editor.getMarkdown();

// Write
editor.commands.setContent(markdownString);

Try this if you want to ... ​

Released under the MIT License.