Docs
Markdown & HTML

Markdown & HTML

It's possible to export or import Blocks to and from Markdown and HTML.

⚠️

The functions to import/export to and from Markdown/HTML are considered "lossy"; some information might be dropped when you export Blocks to those formats.

To serialize Blocks to a non-lossy format (for example, to store the contents of the editor in your backend), simply export the built-in Block format using JSON.stringify(editor.document).

Markdown

BlockNote can import / export Blocks to and from Markdown. Note that this is also considered "lossy", as not all structures can be entirely represented in Markdown.

Converting Blocks to Markdown

blocksToMarkdownLossy converts Block objects to a Markdown string:

blocksToMarkdownLossy(blocks?: Block[]): string;
 
// Usage
const markdownFromBlocks = editor.blocksToMarkdownLossy(blocks);

blocks: The blocks to convert. If not provided, the entire document (all top-level blocks) is used.

returns: The blocks, serialized as a Markdown string.

The output is simplified as Markdown does not support all features of BlockNote (e.g.: children of blocks which aren't list items are un-nested and certain styles are removed).

Demo

import "@blocknote/core/fonts/inter.css";
import { useCreateBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import { useState } from "react";
 
import "./styles.css";
 
export default function App() {
  // Stores the editor's contents as Markdown.
  const [markdown, setMarkdown] = useState<string>("");
 
  // Creates a new editor instance with some initial content.
  const editor = useCreateBlockNote({
    initialContent: [
      {
        type: "paragraph",
        content: [
          "Hello, ",
          {
            type: "text",
            text: "world!",
            styles: {
              bold: true,
            },
          },
        ],
      },
    ],
  });
 
  const onChange = async () => {
    // Converts the editor's contents from Block objects to Markdown and store to state.
    const markdown = await editor.blocksToMarkdownLossy(editor.document);
    setMarkdown(markdown);
  };
 
  // Renders the editor instance, and its contents as Markdown below.
  return (
    <div className={"wrapper"}>
      <div>Input (BlockNote Editor):</div>
      <div className={"item"}>
        <BlockNoteView editor={editor} onChange={onChange} />
      </div>
      <div>Output (Markdown):</div>
      <div className={"item bordered"}>
        <pre>
          <code>{markdown}</code>
        </pre>
      </div>
    </div>
  );
}
 

Parsing Markdown to Blocks

Use tryParseMarkdownToBlocks to try parsing a Markdown string into Block objects:

tryParseMarkdownToBlocks(markdown: string): Blocks[];
 
// Usage
const blocksFromMarkdown = editor.tryParseMarkdownToBlocks(markdown);

returns: The blocks parsed from the Markdown string.

Tries to create Block and InlineContent objects based on Markdown syntax, though not all symbols are recognized. If BlockNote doesn't recognize a symbol, it will parse it as text.

Demo

import "@blocknote/core/fonts/inter.css";
import { useCreateBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import { ChangeEvent, useCallback, useEffect } from "react";
 
const initialMarkdown = "Hello, **world!**";
 
export default function App() {
  // Creates a new editor instance.
  const editor = useCreateBlockNote();
 
  const markdownInputChanged = useCallback(
    async (e: ChangeEvent<HTMLTextAreaElement>) => {
      // Whenever the current Markdown content changes, converts it to an array of
      // Block objects and replaces the editor's content with them.
      const blocks = await editor.tryParseMarkdownToBlocks(e.target.value);
      editor.replaceBlocks(editor.document, blocks);
    },
    [editor]
  );
 
  // For initialization; on mount, convert the initial Markdown to blocks and replace the default editor's content
  useEffect(() => {
    async function loadInitialHTML() {
      const blocks = await editor.tryParseMarkdownToBlocks(initialMarkdown);
      editor.replaceBlocks(editor.document, blocks);
    }
    loadInitialHTML();
  }, [editor]);
 
  // Renders a text area for you to write/paste Markdown in, and the editor instance
  // below, which displays the current Markdown as blocks.
  return (
    <div className={"wrapper"}>
      <div>Input (Markdown):</div>
      <div className={"item bordered"}>
        <code>
          <textarea
            defaultValue={initialMarkdown}
            onChange={markdownInputChanged}
          />
        </code>
      </div>
      <div>Output (BlockNote Editor):</div>
      <div className={"item"}>
        <BlockNoteView editor={editor} editable={false} />
      </div>
    </div>
  );
}
 

Export HTML (for static rendering)

Use blocksToFullHTML to export the entire document with all structure, styles and formatting. The exported HTML is the same as BlockNote would use to render the editor, and includes all structure for nested blocks.

For example, you an use this for static rendering documents that have been created in the editor. Make sure to include the same stylesheets when you want to render the output HTML (see example).

blocksToFullHTML(blocks?: Block[]): string;
 
// Usage
const HTMLFromBlocks = editor.blocksToFullHTML(blocks);

blocks: The blocks to convert. If not provided, the entire document (all top-level blocks) is used.

returns: The blocks, exported to an HTML string.

HTML (for interoperability)

The editor exposes functions to convert Blocks to and from HTML for interoperability with other applications.

Converting Blocks to HTML this way will lose some information such as the nesting of nodes in order to export a simple HTML structure.

Converting Blocks to HTML

Use blocksToHTMLLossy to export Block objects to an HTML string:

blocksToHTMLLossy(blocks?: Block[]): string;
 
// Usage
const HTMLFromBlocks = editor.blocksToHTMLLossy(blocks);

blocks: The blocks to convert. If not provided, the entire document (all top-level blocks) is used.

returns: The blocks, exported to an HTML string.

To better conform to HTML standards, children of blocks which aren't list items are un-nested in the output HTML.

Demo

import "@blocknote/core/fonts/inter.css";
import { useCreateBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import { useState } from "react";
 
import "./styles.css";
 
export default function App() {
  // Stores the editor's contents as HTML.
  const [html, setHTML] = useState<string>("");
 
  // Creates a new editor instance with some initial content.
  const editor = useCreateBlockNote({
    initialContent: [
      {
        type: "paragraph",
        content: [
          "Hello, ",
          {
            type: "text",
            text: "world!",
            styles: {
              bold: true,
            },
          },
        ],
      },
    ],
  });
 
  const onChange = async () => {
    // Converts the editor's contents from Block objects to HTML and store to state.
    const html = await editor.blocksToHTMLLossy(editor.document);
    setHTML(html);
  };
 
  // Renders the editor instance, and its contents as HTML below.
  return (
    <div className="wrapper">
      <div>Input (BlockNote Editor):</div>
      <div className="item">
        <BlockNoteView editor={editor} onChange={onChange} />
      </div>
      <div>Output (HTML):</div>
      <div className="item bordered">
        <pre>
          <code>{html}</code>
        </pre>
      </div>
    </div>
  );
}
 

Parsing HTML to Blocks

Use tryParseHTMLToBlocks to parse an HTML string to Block objects:

tryParseHTMLToBlocks(html: string): Blocks[];
 
// Usage
const blocksFromHTML = editor.tryParseHTMLToBlocks(html);

returns: The blocks parsed from the HTML string.

Tries to create Block objects out of any HTML block-level elements, and InlineContent objects from any HTML inline elements, though not all HTML tags are recognized. If BlockNote doesn't recognize an element's tag, it will parse it as a paragraph or plain text.

Demo

import "@blocknote/core/fonts/inter.css";
import { useCreateBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import { ChangeEvent, useCallback, useEffect } from "react";
 
import "./styles.css";
 
const initialHTML = "<p>Hello, <strong>world!</strong></p>";
 
export default function App() {
  // Creates a new editor instance.
  const editor = useCreateBlockNote();
 
  const htmlInputChanged = useCallback(
    async (e: ChangeEvent<HTMLTextAreaElement>) => {
      // Whenever the current HTML content changes, converts it to an array of
      // Block objects and replaces the editor's content with them.
      const blocks = await editor.tryParseHTMLToBlocks(e.target.value);
      editor.replaceBlocks(editor.document, blocks);
    },
    [editor]
  );
 
  // For initialization; on mount, convert the initial HTML to blocks and replace the default editor's content
  useEffect(() => {
    async function loadInitialHTML() {
      const blocks = await editor.tryParseHTMLToBlocks(initialHTML);
      editor.replaceBlocks(editor.document, blocks);
    }
    loadInitialHTML();
  }, [editor]);
 
  // Renders a text area for you to write/paste HTML in, and the editor instance
  // below, which displays the current HTML as blocks.
  return (
    <div className={"wrapper"}>
      <div>Input (HTML):</div>
      <div className={"item bordered"}>
        <code>
          <textarea defaultValue={initialHTML} onChange={htmlInputChanged} />
        </code>
      </div>
      <div>Output (BlockNote Editor):</div>
      <div className={"item"}>
        <BlockNoteView editor={editor} editable={false} />
      </div>
    </div>
  );
}