Laptop showing a block-based editor on a writer's desk with plugin icons

Every developer who has tried to build a rich text editor from scratch knows the special kind of pain that comes with it. Contenteditable is a minefield. Cursor management is an art form. And the moment someone asks for "just a simple Notion-like editor," you know you are in for weeks of work. Yoopta Editor steps in to absorb that pain. Built on Slate.js, it provides a headless, plugin-based block editor for React that ships with over 20 plugins, slash commands, drag-and-drop, real-time collaboration, and multi-format export. Whether you are building a CMS, a documentation tool, or a collaborative writing app, Yoopta gives you the building blocks without locking you into a specific UI.

The Plugin Buffet

Yoopta's architecture revolves around plugins. Each block type lives in its own npm package, so you only install what you need. The lineup is impressive:

  • Text blocks: Paragraphs, headings (H1-H3), blockquotes, callouts
  • Lists: Bulleted, numbered, and todo lists
  • Media: Images, video, embeds, file attachments, carousels
  • Structure: Tables, accordions, tabs, steps, dividers
  • Code: Syntax-highlighted code blocks
  • Navigation: Table of contents, links, mentions

Text formatting marks like Bold, Italic, Underline, Strike, CodeMark, and Highlight come from the @yoopta/marks package. The editor also ships with a slash command menu, a floating toolbar, drag-and-drop block reordering, and a selection box for multi-block operations. All of this is optional and composable.

Getting Started

Install the core package along with whichever plugins you need:

npm install @yoopta/editor @yoopta/paragraph @yoopta/headings @yoopta/marks slate slate-react slate-dom
yarn add @yoopta/editor @yoopta/paragraph @yoopta/headings @yoopta/marks slate slate-react slate-dom

Note that slate, slate-react, and slate-dom are peer dependencies you need to provide yourself.

Your First Block Editor

Wiring Up the Basics

The core pattern is straightforward. You create an editor instance with createYooptaEditor, pass in your plugins and marks, and render the <YooptaEditor> component:

import YooptaEditor, { createYooptaEditor } from '@yoopta/editor';
import Paragraph from '@yoopta/paragraph';
import Headings from '@yoopta/headings';
import { Bold, Italic, Underline } from '@yoopta/marks';
import { useMemo } from 'react';

const plugins = [Paragraph, Headings];
const marks = [Bold, Italic, Underline];

function MyEditor() {
  const editor = useMemo(() => createYooptaEditor({ plugins, marks }), []);

  return (
    <YooptaEditor
      editor={editor}
      placeholder="Type / to open the command menu"
    />
  );
}

That gives you a working editor with paragraphs, three heading levels, and basic text formatting. Users can type / to open the slash command menu and pick a block type.

Adding UI Chrome

The headless core renders blocks but no toolbar or action menus by default. To add those, install @yoopta/ui and pass the components as children:

import YooptaEditor, { createYooptaEditor } from '@yoopta/editor';
import { DefaultToolbar, DefaultSlashMenu, DefaultBlockActions } from '@yoopta/ui';
import Paragraph from '@yoopta/paragraph';
import Lists from '@yoopta/lists';
import Blockquote from '@yoopta/blockquote';
import { Bold, Italic, Strike } from '@yoopta/marks';
import { useMemo } from 'react';

const plugins = [Paragraph, Lists.BulletedList, Lists.NumberedList, Blockquote];
const marks = [Bold, Italic, Strike];

function FullEditor() {
  const editor = useMemo(() => createYooptaEditor({ plugins, marks }), []);

  return (
    <YooptaEditor editor={editor}>
      <DefaultToolbar />
      <DefaultSlashMenu />
      <DefaultBlockActions />
    </YooptaEditor>
  );
}

Now you have a floating toolbar that appears on text selection, a slash menu for block insertion, and block action buttons for reordering and deleting.

Listening for Changes

Hook into the editor's event system to persist content or trigger side effects:

function TrackedEditor() {
  const editor = useMemo(() => createYooptaEditor({ plugins, marks }), []);

  const handleChange = (value: Record<string, any>) => {
    console.log('Editor content:', value);
    // Save to your backend, local storage, etc.
  };

  return (
    <YooptaEditor
      editor={editor}
      onChange={handleChange}
      autoFocus
    />
  );
}

Going Deeper

Programmatic Block Control

The editor instance exposes a rich API for manipulating content programmatically. This is useful for building toolbar buttons, keyboard shortcuts, or automated content generation:

import { createYooptaEditor } from '@yoopta/editor';

const editor = createYooptaEditor({ plugins, marks });

// Insert a new heading block
editor.insertBlock({ type: 'Headings', props: { depth: 1 } });

// Toggle the current block between paragraph and blockquote
editor.toggleBlock('Blockquote');

// Batch multiple operations into a single undo step
editor.batchOperations(() => {
  editor.insertBlock({ type: 'Paragraph' });
  editor.insertBlock({ type: 'Paragraph' });
  editor.insertBlock({ type: 'Paragraph' });
});

// Undo the entire batch
editor.undo();

The namespaced APIs give you even finer control. Blocks handles block-level CRUD (insert, delete, move, duplicate, indent, outdent), Elements manages content within blocks, and Marks controls text formatting.

Exporting Content

One of Yoopta's strongest features is multi-format export. The @yoopta/exports package converts editor content to HTML, Markdown, or plain text:

import { html, markdown, plainText } from '@yoopta/exports';

function ExportButtons({ editor }: { editor: ReturnType<typeof createYooptaEditor> }) {
  const handleExportHTML = () => {
    const htmlContent = editor.getHTML();
    console.log(htmlContent);
  };

  const handleExportMarkdown = () => {
    const md = editor.getMarkdown();
    console.log(md);
  };

  const handleExportText = () => {
    const text = editor.getPlainText();
    console.log(text);
  };

  return (
    <div>
      <button onClick={handleExportHTML}>Export HTML</button>
      <button onClick={handleExportMarkdown}>Export Markdown</button>
      <button onClick={handleExportText}>Export Plain Text</button>
    </div>
  );
}

This makes Yoopta a natural fit for CMS applications where content needs to be rendered outside the editor, or for email builders that need HTML output.

Real-Time Collaboration

For multi-user editing, the @yoopta/collaboration package integrates Yjs CRDTs with the editor. You get presence indicators, remote cursors, and conflict-free merging:

import YooptaEditor, { createYooptaEditor } from '@yoopta/editor';
import { YooptaCollaboration } from '@yoopta/collaboration';
import * as Y from 'yjs';
import { WebsocketProvider } from 'y-websocket';

function CollaborativeEditor() {
  const ydoc = useMemo(() => new Y.Doc(), []);
  const provider = useMemo(
    () => new WebsocketProvider('ws://localhost:1234', 'my-document', ydoc),
    [ydoc]
  );

  const editor = useMemo(() => createYooptaEditor({ plugins, marks }), []);

  return (
    <YooptaCollaboration editor={editor} ydoc={ydoc} provider={provider}>
      <YooptaEditor editor={editor} />
    </YooptaCollaboration>
  );
}

The collaboration layer handles all the complexity of operational transforms and state synchronization. You just need a Yjs provider (WebSocket, WebRTC, or any other transport) and the rest works.

Theme It Your Way

Version 6 introduced theme presets through @yoopta/themes-shadcn, which gives you a polished shadcn/ui-styled editor out of the box. If the default look does not match your design system, the headless core lets you build completely custom UI from scratch. Every component, from the toolbar to the slash menu to individual block renderers, can be replaced with your own implementation via the renderBlock prop and custom toolbar components.

Wrapping Up

Yoopta Editor occupies a sweet spot in the React editor landscape. It is more opinionated than raw Slate.js but more flexible than fully baked editors. The plugin-per-block-type architecture keeps your bundle lean, the headless core gives you full UI control when you need it, and the pre-built components let you ship fast when you do not. With collaboration support, multi-format export, and an actively growing plugin ecosystem, @yoopta/editor is a strong choice for any React project that needs a block-based editing experience. If you have ever looked at Notion's editor and thought "I want that in my app," Yoopta is the library that gets you there without reinventing the wheel.