Markdown is an export and an import, not the stored format. The document
stays JSON; contentToMarkdown writes GFM and contentFromMarkdown reads
it, over the registry's own features.
## Writing inside your own app Rich text with **bold**, _italic_ and a [link](https://ui.hopla.llc). Type / for a block, or select text for the floating toolbar. <callout icon="💡"> Every block on this page is one feature definition. </callout> * Lists, with Tab to nest * Tables, images, embeds > And a quote, for when someone else said it better.
import { contentFromMarkdown, contentToMarkdown } from "@hoplallc/ui/content-editor";
const markdown = contentToMarkdown(value, { features: FEATURES });
const value = contentFromMarkdown(markdown, { features: FEATURES });What crosses how
Paragraphs, marks, headings, links, lists, quotes, dividers, tables and
fenced code are GFM. An image is an mdast image, its caption as the title. A callout and the
four embeds, which Markdown has no words for, cross as MDX elements named
after their type (<callout icon="💡">, <youtube-video videoId="…" />): a
plain Markdown reader sees the tag, an MDX reader gets the node back.
What Markdown loses is named on each rule (markdown.loss): an image's
width and height today. A test asserts that loss, so a Plate upgrade that
starts carrying it shows up red rather than silently.
Importing existing content
contentFromMarkdown reads Markdown a human wrote: # headings, -
lists, **bold**, links. A newline inside a paragraph is a soft break and
comes back as a space, as CommonMark renders it; a hard break (two trailing
spaces, a backslash before the newline, or <br>) stays a line break, and
contentToMarkdown writes it back as a backslash break. Run scrubImportedContent over the result when the
source carried stray line breaks or zero-width characters, the usual
souvenirs of a migration.