# Introduction Introduction to BlockNote [#introduction-to-blocknote]
NPM {" "} GitHub Repo stars
BlockNote is a block-based rich-text editor for [React](https://reactjs.org/), focused on providing a great out-of-the-box experience with minimal setup. With BlockNote, we want to make it easy for developers to add a next-generation text editing experience to their app, with a UX that's on-par with industry leaders like Notion, Google Docs or Coda. Unlike other rich-text editor libraries, BlockNote organizes documents into blocks. This makes it easy for the user to organize their document, and for developers to interact with the document from code. BlockNote has been created with extensibility in mind. You can customize the document, create custom block types and customize UX elements like menu items. Advanced users can even create their own UI from scratch and use BlockNote with vanilla JavaScript instead of React. * Jump right into the [quickstart](/docs/getting-started) to get started * Learn about [blocks and the editor basics](/docs/foundations/document-structure) and how to interact with the editor using the [editor API](/docs/reference/editor/manipulating-content) * See [UI Components](/docs/react/components) to customize built-in menus and toolbars and [Styling & Theming](/docs/react/styling-theming) to customize the look and feel of the editor * Further extend the editor with your own Blocks using [Custom Schemas](/docs/features/custom-schemas) or add [Real-Time Collaboration](/docs/features/collaboration) Why BlockNote? [#why-blocknote] There are plenty of libraries out there for creating rich-text editors. In fact, BlockNote is built on top of the widely used [ProseMirror](https://prosemirror.net/) and [TipTap](https://tiptap.dev/). As powerful as they are, these libraries often have quite a steep learning-curve and require you to customize every single detail of your editor. This can require months of specialized work. BlockNote instead, offers a great experience with minimal setup, including a ready-made and animated UI. On top of that, it comes with a modern block-based design. This gives documents more structure, allow for a richer user experience while simultaneously making it easier to customize the editor's functionality. Community [#community] We'd love your feedback! If you have questions, need help, or want to contribute reach out to the community on [Discord](https://discord.gg/Qc2QTTH5dF) and [GitHub](https://github.com/TypeCellOS/BlockNote). Next: Set up BlockNote [#next-set-up-blocknote] See how to set up your own editor in the [Quickstart](/docs/getting-started). Here's a quick sneak peek in case you can't wait! # Extensions Extensions [#extensions] BlockNote includes an extensions system which lets you expand the editor's behaviour. Extensions can include any of the following features: * Keyboard shortcuts * Input rules * [ProseMirror plugins](https://prosemirror.net/docs/ref/#state.Plugin_System) * [TipTap extensions](https://tiptap.dev/docs/editor/extensions/custom-extensions/create-new) Creating an extension [#creating-an-extension] You can create extensions using the `createExtension` function: ```typescript type Extension = { key: string; keyboardShortcuts?: Record< string, (ctx: { editor: BlockNoteEditor; }) => boolean >; inputRules?: { find: RegExp; replace: (props: { match: RegExpMatchArray; range: { from: number; to: number }; editor: BlockNoteEditor; }) => PartialBlock | undefined; }[]; plugins?: Plugin[]; tiptapExtensions?: AnyExtension[]; } const CustomExtension = createExtension({ key: "customBlockExtension", keyboardShortcuts: ..., inputRules: ..., plugins: ..., tiptapExtensions: ..., }); ``` Let's go over the options that can be passed into `createExtension`: `key:` The name of the extension. `keyboardShortcuts?:` Keyboard shortcuts can be used to run code when a key combination is pressed in the editor. The key names are the same as those used in the [`KeyboardEvent.key` property](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values). Takes an object which maps key name combinations (e.g. `Meta+Shift+ArrowDown`) to functions, which return `true` when the key press event is handled, or `false` otherwise. The functions have a single argument: * `ctx:` An object containing the BlockNote editor instance. `inputRules?:` Input rules update blocks when given regular expressions are found in them. Takes an array of objects. Each object has a `find` field for the regular expression to find, and a `replace` field, for a function that should run on a match. The function should return a [`PartialBlock`](docs/reference/editor/manipulating-content#partial-blocks) which specifies how the block should be updated, or avoid updating it. It also has a single argument: * `props:` An object containing the result of the regular expression match, a range for the [Prosemirror position indices](https://prosemirror.net/docs/guide/#doc.indexing) spanned by the match, and the BlockNote editor instance. `plugins?:` An array of [ProseMirror plugins](https://prosemirror.net/docs/ref/#state.Plugin_System). `tiptapExtensions?:` An array of [TipTap extensions](https://tiptap.dev/docs/editor/extensions/custom-extensions/create-new). Adding extensions to the editor [#adding-extensions-to-the-editor] Extensions can be added to the editor on their own via the [editor options](/docs/reference/editor/overview#options) or as part of [custom blocks](/docs/features/custom-schemas/custom-blocks). Adding directly to the editor [#adding-directly-to-the-editor] The `extensions` [editor option](/docs/reference/editor/overview#options) takes an array of extensions to be added to the editor: ```typescript const editor = useCreateBlockNote({ extensions: [ // Add extensions here: createExtension({ ... }) ], }); ``` Adding to custom blocks [#adding-to-custom-blocks] When creating a [custom block](/docs/features/custom-schemas/custom-blocks#creating-a-custom-block-type) using `createReactBlockSpec`, you can pass an array of extensions to the third parameter: ```typescript const createCustomBlock = createReactBlockSpec( { // Block config ... }, { // Block implementation ... } [ // Add extensions here: createExtension({ ... }) ], }); ``` # Localization (i18n) Localization (i18n) [#localization-i18n] BlockNote is designed to be fully localized, with support for multiple languages. You can easily change the language of your editor or create custom translations. Supported Languages [#supported-languages] BlockNote supports the following languages out of the box: * **Arabic** (`ar`) - العربية * **Chinese (Simplified)** (`zh`) - 中文 * **Chinese (Traditional)** (`zh-tw`) - 繁體中文 * **Croatian** (`hr`) - Hrvatski * **Dutch** (`nl`) - Nederlands * **English** (`en`) - English * **French** (`fr`) - Français * **German** (`de`) - Deutsch * **Hebrew** (`he`) - עברית * **Icelandic** (`is`) - Íslenska * **Italian** (`it`) - Italiano * **Japanese** (`ja`) - 日本語 * **Korean** (`ko`) - 한국어 * **Norwegian** (`no`) - Norsk * **Polish** (`pl`) - Polski * **Portuguese** (`pt`) - Português * **Russian** (`ru`) - Русский * **Slovak** (`sk`) - Slovenčina * **Spanish** (`es`) - Español * **Ukrainian** (`uk`) - Українська * **Uzbek** (`uz`) - O'zbekcha * **Vietnamese** (`vi`) - Tiếng Việt Basic Usage [#basic-usage] To use a different language, import the desired locale and pass it to the `dictionary` option: ```tsx import { useCreateBlockNote, BlockNoteView } from "@blocknote/react"; import { fr } from "@blocknote/core/locales"; function FrenchEditor() { const editor = useCreateBlockNote({ dictionary: fr, }); return ; } ``` Dynamic Language Switching [#dynamic-language-switching] You can dynamically change the language based on user preferences or your app's locale: ```tsx import { useCreateBlockNote, BlockNoteView } from "@blocknote/react"; import * as locales from "@blocknote/core/locales"; function LocalizedEditor({ language = "en" }) { const editor = useCreateBlockNote({ dictionary: locales[language as keyof typeof locales] || locales.en, }); return ; } // Usage ``` Customizing Text Strings [#customizing-text-strings] You can customize specific text strings by extending an existing dictionary: ```tsx import { useCreateBlockNote, BlockNoteView } from "@blocknote/react"; import { en } from "@blocknote/core/locales"; function CustomEditor() { const editor = useCreateBlockNote({ dictionary: { ...en, placeholders: { ...en.placeholders, default: "Start typing your story...", heading: "Enter your title here", emptyDocument: "Begin your document", }, slash_menu: { ...en.slash_menu, paragraph: { ...en.slash_menu.paragraph, title: "Text Block", subtext: "Regular text content", }, }, }, }); return ; } ``` Dictionary Structure [#dictionary-structure] The dictionary object contains translations for various parts of the editor: Placeholders [#placeholders] Text that appears when blocks are empty: ```tsx placeholders: { default: "Enter text or type '/' for commands", heading: "Heading", bulletListItem: "List", numberedListItem: "List", checkListItem: "List", new_comment: "Write a comment...", edit_comment: "Edit comment...", comment_reply: "Add comment...", } ``` Slash Menu [#slash-menu] Commands that appear when typing `/`: ```tsx slash_menu: { paragraph: { title: "Paragraph", subtext: "The body of your document", aliases: ["p", "paragraph"], group: "Basic blocks", }, heading: { title: "Heading 1", subtext: "Top-level heading", aliases: ["h", "heading1", "h1"], group: "Headings", }, // ... more menu items } ``` UI Elements [#ui-elements] Text for buttons, menus, and other interface elements: ```tsx side_menu: { add_block_label: "Add block", drag_handle_label: "Open block menu", }, table_handle: { delete_column_menuitem: "Delete column", add_left_menuitem: "Add column left", // ... more table options }, color_picker: { text_title: "Text", background_title: "Background", colors: { default: "Default", gray: "Gray", // ... more colors }, } ``` Integration with i18n Libraries [#integration-with-i18n-libraries] You can integrate BlockNote with popular i18n libraries like `react-i18next` or `next-intl`: ```tsx import { useCreateBlockNote, BlockNoteView } from "@blocknote/react"; import { useTranslation } from "react-i18next"; import * as locales from "@blocknote/core/locales"; function I18nEditor() { const { i18n } = useTranslation(); const editor = useCreateBlockNote({ dictionary: locales[i18n.language as keyof typeof locales] || locales.en, }); return ; } ``` Adding New Languages [#adding-new-languages] To add support for a new language, you can: 1. **Submit a Pull Request** to the BlockNote repository with your translations 2. **Create a custom dictionary** in your application for immediate use When creating translations, make sure to: * Translate all text strings in the dictionary * Maintain the same structure as the English dictionary * Test the translations with different content types * Consider cultural differences in UI text Examples [#examples] Basic Localization [#basic-localization] Custom Placeholders [#custom-placeholders] # Server-side processing Server-side Processing [#server-side-processing] While you can use the `BlockNoteEditor` on the client side, you can also use `ServerBlockNoteEditor` from `@blocknote/server-util` to process BlockNote documents on the server. For example, use the following code to convert a BlockNote document to HTML on the server: ```tsx import { ServerBlockNoteEditor } from "@blocknote/server-util"; const editor = ServerBlockNoteEditor.create(); const html = await editor.blocksToFullHTML(blocks); ``` `ServerBlockNoteEditor.create` takes the same BlockNoteEditorOptions as `useCreateBlockNote` and `BlockNoteEditor.create` ([see docs](/docs/getting-started)), so you can pass the same configuration (for example, your custom schema) to your server-side BlockNote editor as on the client. Functions for converting blocks [#functions-for-converting-blocks] `ServerBlockNoteEditor` exposes the same functions for converting blocks as the client side editor ([HTML](/docs/features/import/html), [Markdown](/docs/features/import/markdown)): * `blocksToFullHTML` * `blocksToHTMLLossy` and `tryParseHTMLToBlocks` * `blocksToMarkdownLossy` and `tryParseMarkdownToBlocks` Yjs processing [#yjs-processing] Additionally, `ServerBlockNoteEditor` provides functions for processing Yjs documents in case you use Yjs collaboration: * `yDocToBlocks` or `yXmlFragmentToBlocks`: use this to convert a Yjs document or XML Fragment to BlockNote blocks * `blocksToYDoc` or `blocksToYXmlFragment`: use this to convert a BlockNote document (blocks) to a Yjs document or XML Fragment React compatibility [#react-compatibility] If you use [custom schemas in React](/docs/features/custom-schemas), you can use the same schema on the server side. Functions like `blocksToFullHTML` will use your custom React rendering functions to export blocks to HTML, similar to how these functions work on the client. However, it could be that your React components require access to a React context (e.g. a theme or localization context). For these use-cases, we provide a function `withReactContext` that allows you to pass a React context to the server-side editor. This example exports a BlockNote document to HTML within a React context `YourContext`, so that even Custom Blocks built in React that require `YourContext` will be exported correctly: ```tsx const html = await editor.withReactContext( ({ children }) => ( {children} ), async () => editor.blocksToFullHTML(blocks), ); ``` Next.js App Router [#nextjs-app-router] If you're using `@blocknote/server-util` in a Next.js App Router API route (Route Handler), you need to add the BlockNote packages to `serverExternalPackages` in your `next.config.ts`: ```typescript import type { NextConfig } from "next"; const nextConfig: NextConfig = { serverExternalPackages: [ "@blocknote/core", "@blocknote/react", "@blocknote/server-util", ], }; export default nextConfig; ``` # Document Structure Document Structure [#document-structure] Each BlockNote document is made up of a list of blocks. A block is a piece of content like a paragraph, heading, list item or image. Blocks can be dragged around by users in the editor. A block contains a piece of content and optionally nested (child) blocks: Blocks [#blocks] The `Block` type is used to describe any given block in the editor: ```typescript type Block = { id: string; type: string; props: Record; content: InlineContent[] | TableContent | undefined; children: Block[]; }; ``` Block Properties [#block-properties] * **`id`**: The block's ID. Multiple blocks cannot share a single ID, and a block will keep the same ID from when it's created until it's removed. * **`type`**: The block's type, such as a paragraph, heading, or list item. For an overview of built-in block types, see [Built-in Blocks](/docs/features/blocks). * **`props`**: The block's properties, which is a set of key/value pairs that further specify how the block looks and behaves. Different block types have different props - see [Built-in Blocks](/docs/features/blocks) for more. * **`content`**: The block's rich text content, usually represented as an array of `InlineContent` objects. This does not include content from any nested blocks. Read on to [Inline Content](#inline-content) for more on this. * **`children`**: Any blocks nested inside the block. The nested blocks are also represented using `Block` objects. Inline Content [#inline-content] The `content` field of a block contains the rich-text content of a block. This is defined as an array of `InlineContent` objects. Inline content can either be styled text or a link (or a custom inline content type if you customize the editor schema). Inline Content Objects [#inline-content-objects] The `InlineContent` type is used to describe a piece of inline content: ```typescript type Link = { type: "link"; content: StyledText[]; href: string; }; type StyledText = { type: "text"; text: string; styles: Styles; }; type CustomInlineContent = { type: string; content: StyledText[] | string | undefined; props: Record; }; type InlineContent = Link | StyledText | CustomInlineContent; ``` The `styles` property is explained below. Styles and Rich Text [#styles-and-rich-text] The `styles` property of `StyledText` objects is used to describe the rich text styles (e.g.: bold, italic, color) or other attributes of a piece of text. It's a set of key / value pairs that specify the styles applied to the text. See the [Default Styles](/docs/features/blocks/inline-content#default-styles) to learn which styles are included in BlockNote by default. See it for yourself [#see-it-for-yourself] The demo below shows the editor contents (document) in JSON. It's an array of `Block` objects that updates as you type in the editor: Special Cases [#special-cases] While most blocks use an array of `InlineContent` objects to describe their content (e.g.: paragraphs, headings, list items), some blocks, like [images](/docs/features/blocks/embeds#image), don't contain any rich text content, so their `content` fields will be `undefined`. There are a few other cases where `content` will not contain `InlineContent`. Plain Text Content [#plain-text-content] Some blocks, like [code blocks](/docs/features/blocks/code-blocks), store their content as plain text rather than rich text. Their `content` is still an array of `StyledText` objects, but the text is always unstyled (its `styles` is an empty object) and can't contain links or other inline content: ```typescript type PlainContent = { type: "text"; text: string; styles: {}; }[]; ``` To read a plain block's text, use `plainContentToString`: ```typescript import { plainContentToString } from "@blocknote/core"; const text = plainContentToString(block.content); ``` [Custom inline content](/docs/features/custom-schemas/custom-inline-content) can hold plain text too, but there it's represented directly as a `string` rather than an array. Custom blocks and inline content opt into plain text content by setting `content: "plain"` - see [Custom Blocks](/docs/features/custom-schemas/custom-blocks) and [Custom Inline Content](/docs/features/custom-schemas/custom-inline-content). Column Blocks [#column-blocks] The `@blocknote/xl-multi-column` package allows you to organize blocks side-by-side in columns. It introduces 2 additional block types, the column and column list: ```typescript type ColumnBlock = { id: string; type: "column"; props: { width: number }; content: undefined; children: Block[]; }; type ColumnListBlock = { id: string; type: "columnList"; props: {}; content: undefined; children: ColumnBlock[]; }; ``` While both of these act as regular blocks, there are a few additional restrictions to have in mind when working with them: * Children of columns must be regular blocks * Children of column lists must be columns * There must be at least 2 columns in a column list Tables [#tables] [Tables](/docs/features/blocks/tables) are also different, as they contain `TableContent`. Here, each table cell is represented as an array of `InlineContent` objects: ```typescript type TableContent = { type: "tableContent"; columnWidths: (number | undefined)[]; headerRows?: number; headerCols?: number; rows: { cells: InlineContent[][]; }[]; }; ``` # Manipulating Blocks Manipulating Blocks [#manipulating-blocks] BlockNote operates on a **block-based architecture**, where all content is organized into discrete blocks. Understanding how to manipulate these blocks is fundamental to working with BlockNote effectively. Block-Based Architecture [#block-based-architecture] In BlockNote, everything is a block. A paragraph is a block, a heading is a block, a list item is a block, and even complex structures like tables are composed of blocks. This unified approach makes document manipulation consistent and predictable. Core Block Operations [#core-block-operations] BlockNote provides a comprehensive set of operations for manipulating blocks, all working at the block level: Reading Blocks [#reading-blocks] * **Get the entire document** - Retrieve all top-level blocks * **Get specific blocks** - Access individual blocks by ID or reference * **Navigate relationships** - Find previous, next, or parent blocks * **Traverse all blocks** - Iterate through the entire document structure [See more in the API reference](/docs/reference/editor/manipulating-content#reading-blocks) ```typescript // Get the entire document const document: Block[] = editor.document; // Get a specific block const block = editor.getBlock(blockId); // Get the previous block const previousBlock = editor.getPreviousBlock(blockId); // Get the next block const nextBlock = editor.getNextBlock(blockId); ``` Creating Blocks [#creating-blocks] * **Insert new blocks** - Add blocks before or after existing ones * **Create complex structures** - Build nested blocks like lists and tables * **Generate blocks programmatically** - Create blocks from data or user input [See more in the API reference](/docs/reference/editor/manipulating-content#inserting-blocks) ```typescript // Insert a simple paragraph block editor.insertBlocks( [{ type: "paragraph", content: "Hello, world!" }], referenceBlock, ); // Create a complex block structure editor.insertBlocks( [ { type: "heading", content: "My Heading" }, { type: "paragraph", content: "Some content" }, { type: "bulletListItem", content: "List item 1" }, { type: "bulletListItem", content: "List item 2" }, ], referenceBlock, ); ``` Modifying Blocks [#modifying-blocks] * **Update existing blocks** - Change block type, content, or properties * **Replace blocks** - Swap one or more blocks with new blocks * **Move blocks** - Reorder blocks by moving them up or down * **Nest and unnest** - Change the hierarchy by indenting or outdenting blocks [See more in the API reference](/docs/reference/editor/manipulating-content#updating-blocks) ```typescript // Change a block's type editor.updateBlock(blockId, { type: "heading" }); // Update block content and properties editor.updateBlock(blockId, { content: "Updated content", props: { level: 2 }, }); ``` Removing Blocks [#removing-blocks] * **Delete specific blocks** - Remove individual blocks or groups of blocks * **Clear selections** - Remove blocks based on user selection [See more in the API reference](/docs/reference/editor/manipulating-content#removing-blocks) ```typescript // Remove specific blocks editor.removeBlocks([blockId1, blockId2]); // Replace blocks with new blocks editor.replaceBlocks( [oldBlockId], [{ type: "paragraph", content: "New content" }], ); ``` Working with Cursor and Selections [#working-with-cursor-and-selections] * **Read cursor position** - Get information about where the user's cursor is located * **Set cursor position** - Move the cursor to specific blocks * **Read selections** - Access blocks currently selected by the user * **Set selections** - Programmatically select ranges of blocks [See more in the API reference](/docs/reference/editor/cursor-selections) ```typescript // Get cursor position information const cursorPosition = editor.getTextCursorPosition(); // Set cursor to a specific block editor.setTextCursorPosition(blockId, "start"); // Get current selection const selection = editor.getSelection(); // Set selection programmatically editor.setSelection(startBlockId, endBlockId); ``` Best Practices [#best-practices] 1. **Work with block references** - Use existing blocks as references for positioning new blocks 2. **Handle errors gracefully** - Operations can fail if blocks don't exist or are invalid 3. **Consider user experience** - Think about how your block manipulations affect the user's workflow 4. **Group related operations** - Use [transactions](/docs/reference/editor/overview#transactions) to group multiple block changes into a single undo/redo operation Next Steps [#next-steps] This overview covers the fundamental concepts of block manipulation in BlockNote. For detailed API reference and specific examples, see: * [Manipulating Blocks Reference](/docs/reference/editor/manipulating-content) - Complete API documentation * [Cursor & Selections](/docs/reference/editor/cursor-selections) - Working with user selections * [Block Types](/docs/features/blocks) - Understanding different block types and their properties # Schemas Schemas [#schemas] Schemas are the core of how BlockNote works. They are the basic building blocks of the editor, and are used to define the content of the editor. The schema is a collection of definitions for the different types of blocks, inline content, and styles that can be used in the editor. By default, BlockNote contains everything found under the [Built-in Blocks](/docs/features/blocks) section. You can also modify this default schema, or create your own from scratch - see [Custom Schemas](/docs/features/custom-schemas) to learn how. # Format Interoperability Format Interoperability [#format-interoperability] BlockNote is compatible with a few different storage formats, each with its own advantages and disadvantages. This guide will show you how to use each of them. Overview [#overview] When it comes to editors, formats can be tricky. The editor needs to be able to both read and write to each format. If elements are not preserved in this transformation, we call the conversion *lossy*. While we'd ideally support every format, **other formats may not support all BlockNote content.** See the table below for a summary of the formats we support and their lossiness: | Format | Import | Export | [Pro Only](/pricing) | | :------------------------------------------------------------------------ | :-------- | :-------- | :------------------- | | **BlockNote JSON (`editor.document`)** | ✅ | ✅ | ❌ | | **BlockNote HTML (`blocksToFullHTML`)** | ✅ | ✅ | ❌ | | **Standard HTML (`blocksToHTMLLossy`)** | ✅ (lossy) | ✅ (lossy) | ❌ | | **Markdown (`blocksToMarkdownLossy`)** | ✅ (lossy) | ✅ (lossy) | ❌ | | **[PDF](/docs/features/export/pdf)** (`@blocknote/xl-pdf-exporter`) | ❌ | ✅ | ✅ | | **[DOCX](/docs/features/export/docx)** (`@blocknote/xl-docx-exporter`) | ❌ | ✅ | ✅ | | **[ODT](/docs/features/export/odt)** (`@blocknote/xl-odt-exporter`) | ❌ | ✅ | ✅ | | **[Email](/docs/features/export/email)** (`@blocknote/xl-email-exporter`) | ❌ | ✅ | ✅ | | **[Typst](/docs/features/export/typst)** (`@blocknote/xl-typst-exporter`) | ❌ | ✅ | ✅ | **Tip:** It's recommended to use **BlockNote JSON (`editor.document`)** for storing your documents, as it's the most durable format & guaranteed to be lossless. Working with Blocks (JSON) [#working-with-blocks-json] BlockNote uses a JSON structure (an array of `Block` objects) as its native format. This is the recommended way to store documents as it's **lossless**, preserving the exact structure and all attributes of your content. Saving Blocks [#saving-blocks] The best way to get the latest content is to use the `editor.onChange` callback if using vanilla JS or `useEditorChange` hook if using React. This function is called every time the editor's content changes. ```tsx twoslash import React from "react"; import { useCreateBlockNote, useEditorChange } from "@blocknote/react"; import { BlockNoteView } from "@blocknote/mantine"; // ---cut-start--- function storeToDB(blocks: string) { console.log(blocks); } // ---cut-end--- export default function App() { const editor = useCreateBlockNote(); useEditorChange((editor) => { // The current document content as a string const savedBlocks = JSON.stringify(editor.document); // ^^^^^^^^^^^^^^^ storeToDB(savedBlocks); }, editor); return ; } ``` Loading Blocks [#loading-blocks] To load content, you can use the `initialContent` prop when creating the editor. You can pass the array of `Block` objects you previously saved. ```tsx import { useCreateBlockNote } from "@blocknote/react"; import type { Block } from "@blocknote/core"; import { BlockNoteView } from "@blocknote/mantine"; export default function App({ initialContent, }: { initialContent?: Block[]; }) { const editor = useCreateBlockNote({ initialContent, }); return ; } ``` Working with HTML [#working-with-html] BlockNote provides utilities to convert content between `Block` objects and HTML. Note that converting to standard HTML can be **lossy**. Saving as HTML [#saving-as-html] To convert the document to an HTML string, you can use `editor.blocksToFullHTML(blocks: Block[])`: ```tsx twoslash import React from "react"; import { useCreateBlockNote, useEditorChange } from "@blocknote/react"; import { BlockNoteView } from "@blocknote/mantine"; // ---cut-start--- function storeToDB(html: string) { console.log(html); } // ---cut-end--- export default function App() { const editor = useCreateBlockNote(); useEditorChange(async (editor) => { const html = await editor.blocksToFullHTML(editor.document); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // You can now save this HTML string storeToDB(html); }, editor); return ; } ``` The `editor.blocksToFullHTML` method will output HTML in the BlockNote internal format. If you want to export to standard HTML, you can use `editor.blocksToHTMLLossy` instead. Loading from HTML [#loading-from-html] To load HTML content, you first need to convert it to an array of `Block` objects using `editor.tryParseHTMLToBlocks()`. Then, you can insert it into the editor. ```tsx twoslash import React from "react"; import { useEffect } from "react"; import { useCreateBlockNote } from "@blocknote/react"; import { BlockNoteView } from "@blocknote/mantine"; const myHTML = "

This is a paragraph.

"; export default function App() { const editor = useCreateBlockNote(); useEffect(() => { // Replaces the blocks on initialization // But, you can also call this before rendering the editor async function loadHTML() { const blocks = await editor.tryParseHTMLToBlocks(myHTML); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ editor.replaceBlocks(editor.document, blocks); } loadHTML(); }, [editor]); return ; } ``` Working with Markdown [#working-with-markdown] BlockNote also supports converting to and from Markdown. However, converting to and from Markdown is a **lossy** conversion. BlockNote ships a **minimal** Markdown parser/serializer that targets the common CommonMark + GFM subset (headings, paragraphs, lists, task lists, tables, code, blockquotes, links, images, emphasis, strikethrough, hard breaks). Supporting every Markdown dialect (CommonMark, GFM, MDX, Pandoc, and various extensions) is not a goal for the editor. If your use case requires Markdown features beyond this subset, **parse the Markdown to HTML yourself** (with a library like [`marked`](https://github.com/markedjs/marked), [`markdown-it`](https://github.com/markdown-it/markdown-it), or [`remark`](https://github.com/remarkjs/remark)) and feed the resulting HTML to `editor.tryParseHTMLToBlocks` — HTML is the format BlockNote uses for arbitrary pastes and has much broader interoperability. Saving as Markdown [#saving-as-markdown] To convert the document to a Markdown string, you can use `editor.blocksToMarkdownLossy()`: ```tsx twoslash import React from "react"; import { useCreateBlockNote, useEditorChange } from "@blocknote/react"; import { BlockNoteView } from "@blocknote/mantine"; // ---cut-start--- function storeToDB(markdown: string) { console.log(markdown); } // ---cut-end--- export default function App() { const editor = useCreateBlockNote(); useEditorChange(async (editor) => { const markdown = await editor.blocksToMarkdownLossy(editor.document); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // You can now save this Markdown string storeToDB(markdown); }, editor); return ; } ``` Loading from Markdown [#loading-from-markdown] To load Markdown content, you first need to convert it to an array of `Block` objects using `editor.tryParseMarkdownToBlocks()`. Then, you can insert it into the editor. ```tsx twoslash import React from "react"; import { useEffect } from "react"; import { useCreateBlockNote } from "@blocknote/react"; import { BlockNoteView } from "@blocknote/mantine"; const myMarkdown = "This is a paragraph with **bold** text."; export default function App() { const editor = useCreateBlockNote(); useEffect(() => { async function loadMarkdown() { const blocks = await editor.tryParseMarkdownToBlocks(myMarkdown); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ editor.replaceBlocks(editor.document, blocks); } loadMarkdown(); }, [editor]); return ; } ``` Export Only [#export-only] BlockNote can also export to these additional formats: * DOCX * Via the [`@blocknote/xl-docx-exporter` package](/docs/features/export/docx) * PDF * Via the [`@blocknote/xl-pdf-exporter` package](/docs/features/export/pdf) * ODT * Via the [`@blocknote/xl-odt-exporter` package](/docs/features/export/odt) * Email * Via the [`@blocknote/xl-email-exporter` package](/docs/features/export/email) * Typst * Via the [`@blocknote/xl-typst-exporter` package](/docs/features/export/typst) # Overview Using BlockNote With React [#using-blocknote-with-react] BlockNote provides a powerful React integration that makes it easy to add rich text editing capabilities to your applications. The React bindings offer a declarative API that integrates seamlessly with React's component model and state management patterns. Key Components [#key-components] The React integration centers around two main pieces: * **`useCreateBlockNote`** - A React hook that creates and manages editor instances * **`BlockNoteView`** - A component that renders the editor with a complete UI Quick Start [#quick-start] Here's a minimal example of how to integrate BlockNote into a React component: ```tsx import React from "react"; import { useCreateBlockNote } from "@blocknote/react"; import { BlockNoteView } from "@blocknote/mantine"; // Or, you can use ariakit, shadcn, etc. function MyEditor() { const editor = useCreateBlockNote(); return ; } ``` This gives you a fully functional editor with: * Text editing and formatting * Block types (paragraphs, headings, lists, etc.) * Toolbar for formatting options * Side menu for block operations BlockNoteView [#blocknoteview] The `` component is used to render the editor. It also provides a number of props for editor specific events. Props [#props] Hooks [#hooks] useCreateBlockNote [#usecreateblocknote] The `useCreateBlockNote` hook is used to create a new `BlockNoteEditor` instance. ```tsx twoslash import React from "react"; /** * The options for the editor, like initial content, schema, etc. * See the [Editor Options API reference](/docs/reference/editor/options) for more details */ type BlockNoteEditorOptions = object; /** * See the [Editor API reference](/docs/reference/editor/manipulate-blocks) for more details */ type BlockNoteEditor = object; /** * This hook creates a new editor instance, but doesn't render it. */ // ---cut--- declare function useCreateBlockNote( options?: BlockNoteEditorOptions, deps?: React.DependencyList, ): BlockNoteEditor; ``` useEditorChange [#useeditorchange] The `useEditorChange` hook is used to listen for changes to the editor. ```tsx twoslash import React from "react"; /** * The blocks that were inserted, updated, or deleted by the change that occurred. * See the [Events documentation](/docs/reference/editor/events#onchange) for more details */ type BlocksChanged = object; /** * See the [Editor API reference](/docs/reference/editor/manipulate-blocks) for more details */ type BlockNoteEditor = object; /** * This hook creates a new editor instance, but doesn't render it. */ // ---cut--- declare function useEditorChange( callback: ( editor: BlockNoteEditor, ctx: { /** * Returns the blocks that were inserted, updated, or deleted by the change that occurred. */ getChanges(): BlocksChanged; }, ) => void, editor?: BlockNoteEditor, ): BlockNoteEditor; ``` useEditorSelectionChange [#useeditorselectionchange] The `useEditorSelectionChange` hook is used to listen for changes to the editor selection. ```tsx twoslash import React from "react"; /** * See the [Editor API reference](/docs/reference/editor/manipulate-blocks) for more details */ type BlockNoteEditor = object; /** * This hook listens for changes to the editor selection. */ // ---cut--- declare function useEditorSelectionChange( /** * Callback that runs when the editor's selection changes. */ callback: () => void, editor?: BlockNoteEditor, ): BlockNoteEditor; ``` Next Steps [#next-steps] The editor is now ready to use! Start typing and explore the various block types and formatting options available in the toolbar. Now that you have a basic editor working, you can explore: * [Built-in Block Types](/docs/features/blocks) - Learn about what types of content the BlockNote editor supports by default * [Styling & Theming](/docs/react/styling-theming) - Customize how the editor looks and feels * [Custom UI Elements](/docs/react/components) - Replace the default UI components to really personalize your editor * [Custom Schemas](/docs/features/custom-schemas) - Expand the types of content that users can add to the editor * [Examples](/examples) - Browse a library of examples created by the BlockNote maintainers and community members # With Ariakit Getting Started With Ariakit [#getting-started-with-ariakit] [Ariakit](https://ariakit.org/) is an open-source library of unstyled (headless), primitive components with a focus on Accessibility. npm pnpm bun ```console npm install @blocknote/core @blocknote/react @blocknote/ariakit ``` ```console pnpm add @blocknote/core @blocknote/react @blocknote/ariakit ``` ```console bun add @blocknote/core @blocknote/react @blocknote/ariakit ``` To use BlockNote with Ariakit, you can import `BlockNoteView` from `@blocknote/ariakit`. You can fully style the components with your own CSS, or import the provided default styles using the `@blocknote/ariakit/style.css` stylesheet. # Editor Setup Editor Setup [#editor-setup] You can customize your editor when you instantiate it. Let's take a closer looks at the basic methods and components to set up your BlockNote editor. Create an editor [#create-an-editor] Create a new `BlockNoteEditor` by calling the `useCreateBlockNote` hook. This instantiates a new editor and its required state. You can later interact with the editor using the Editor API and pass it to the `BlockNoteView` component. ```tsx twoslash import React from "react"; /** * The options for the editor, like initial content, schema, etc. * See the [Editor Options API reference](/docs/reference/editor/overview#options) for more details */ type BlockNoteEditorOptions = object; /** * See the [Editor API reference](/docs/reference/editor/manipulate-blocks) for more details */ type BlockNoteEditor = object; /** * This hook creates a new editor instance, but doesn't render it. */ // ---cut--- declare function useCreateBlockNote( options?: BlockNoteEditorOptions, deps?: React.DependencyList, ): BlockNoteEditor; ``` The hook takes two optional parameters: **options:** Configure the editor with various options. You can find some commonly used options below, or see [Editor Options](/docs/reference/editor/overview#options) for all available options. * `initialContent` - Set starting content * `dictionary` - Customize text strings for localization. See the [Localization](/docs/features/localization) for more. * `schema` - Add custom blocks and styles. See [Custom Schemas](/docs/features/custom-schemas). * `uploadFile` - Handle file uploads to a backend. * `pasteHandler` - Handle how pasted clipboard content gets parsed. **deps:** React dependency array that determines when to recreate the editor. Manually creating the editor ( `BlockNoteEditor.create` )

The `useCreateBlockNote` hook is actually a simple `useMemo` wrapper around the `BlockNoteEditor.create` method. You can use this method directly if you want to control the editor lifecycle manually. For example, we do this in the [Saving & Loading example](/examples/backend/saving-loading) to delay the editor creation until some content has been fetched from an external data source.

Render the editor [#render-the-editor] Use the `` component to render the `BlockNoteEditor` instance you just created: ```tsx const editor = useCreateBlockNote(); return ; ``` The `` component has a number of props that you can use to customize the editor. See [React Overview](/docs/react/overview) for more information. But, here are some important props to consider: * `editor`: The `BlockNoteEditor` instance to render. * `editable`: Whether the editor should be editable. * `onChange`: Callback fired when the editor content (document) changes. * `onSelectionChange`: Callback fired when the editor selection changes. * `theme`: The editor's theme, see [Themes](/docs/react/styling-theming/themes) for more about this. Uncontrolled component

Note that the `BlockNoteView` component is an [uncontrolled component](https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components). This means you don't pass in the editor content directly as a prop. You can use the `initialContent` option in the `useCreateBlockNote` hook to set the initial content of the editor (similar to the `defaultValue` prop in a regular React `