Skip to main content
Yoopta Editor is a React-based rich-text editor built on top of Slate.js with a modular plugin architecture. It provides a flexible and extensible framework for building sophisticated document editors.

Core Architecture

Yoopta Editor follows a layered architecture that separates concerns between blocks, elements, and content management:

Data Model

Yoopta Editor uses a structured data model that consists of blocks and elements:

Content Structure

type YooptaContentValue = Record<string, YooptaBlockData>

type YooptaBlockData = {
  id: string;
  type: string;           // PascalCase: "Paragraph", "HeadingOne"
  value: SlateElement[];  // Slate elements with kebab-case types
  meta: { order, depth, align }
}

type SlateElement = {
  id: string;
  type: string;           // kebab-case: "paragraph", "heading-one"
  children: Descendant[];
  props?: { nodeType: 'block' | 'inline' | 'void', ... }
}
Notice the naming convention: Block types use PascalCase (“Paragraph”), while element types use kebab-case (“paragraph”). This distinction helps differentiate between the block-level API and the Slate-level element API.

Block Metadata

Every block includes metadata for positioning and formatting:
packages/core/editor/src/editor/types.ts
type YooptaBlockBaseMeta = {
  order: number;    // Block position in document
  depth: number;    // Nesting level (0 = root)
  align?: 'left' | 'center' | 'right' | undefined;
};

Monorepo Structure

Yoopta Editor is organized as a monorepo using Turborepo and Yarn Berry:
packages/
├── core/
│   ├── editor/       # @yoopta/editor - Main editor component, YooEditor API
│   ├── collaboration/# @yoopta/collaboration - Real-time collaboration (Yjs)
│   ├── ui/           # @yoopta/ui - Toolbar, ActionMenu, BlockOptions
│   └── exports/      # @yoopta/exports - HTML/Markdown/PlainText serializers
├── plugins/          # Block plugins (20+ available)
├── marks/            # @yoopta/marks - Text formatting
└── themes/           # Theme packages (base, material, shadcn)

Plugin Architecture

Plugins are the building blocks of Yoopta Editor. Each plugin defines:
  • Elements: Visual components and their structure
  • Events: DOM event handlers (keyboard, mouse, etc.)
  • Lifecycle: Hooks for creation and destruction
  • Parsers: HTML/Markdown serialization
  • Commands: Custom operations
  • Extensions: Slate editor customization
Yoopta Editor includes 20+ built-in plugins: accordion, blockquote, callout, carousel, code, divider, embed, emoji, file, headings, image, link, lists, mention, paragraph, steps, table, table-of-contents, tabs, and video.

Slate.js Integration

Each block maintains its own Slate editor instance. This architecture provides:
  • Isolation: Block-level editing doesn’t affect other blocks
  • Performance: Only active blocks need to be rendered
  • Flexibility: Different plugins can customize their Slate behavior
type YooptaPluginsEditorMap = Record<string, SlateEditor>;

// Each block ID maps to its own Slate editor
editor.blockEditorsMap[blockId] // => SlateEditor instance

Operation-Based Updates

Yoopta Editor uses an operation-based system for state updates, similar to Slate.js. All changes flow through operations:
packages/core/editor/src/editor/core/applyTransforms.ts
type YooptaOperation =
  | InsertBlockOperation
  | DeleteBlockOperation
  | SplitBlockOperation
  | MergeBlockOperation
  | ToggleBlockOperation
  | MoveBlockOperation
  | SetBlockValueOperation
  | SetBlockMetaOperation
  | SetSlateOperation
  | SetEditorValueOperation;
This enables:
  • History: Undo/redo support
  • Collaboration: Operational transformation
  • Debugging: Operation logs
  • Testing: Deterministic updates

Namespace APIs

Yoopta Editor provides organized namespace APIs for different operations:
import { Blocks, Elements, Marks, Selection } from '@yoopta/editor';

// Block-level operations
Blocks.insertBlock(editor, { ... });
Blocks.updateBlock(editor, { ... });

// Element-level operations
Elements.insertElement(editor, { ... });
Elements.updateElement(editor, { ... });

// Text formatting
Marks.update(editor, { type: 'bold', value: true });
These namespaces complement the instance methods on the YooEditor object.

Key Design Principles

1. Plugin-First Architecture

Everything is a plugin. Even core functionality like paragraphs and headings are implemented as plugins.

2. Composability

Plugins can be composed and extended. Elements from one plugin can be injected into another.

3. Type Safety

Full TypeScript support with generic types for plugin elements and props.

4. Framework Agnostic Core

While built for React, the core editor logic is framework-agnostic and could be adapted to other frameworks.

5. Performance

Lazy rendering and isolated Slate instances ensure good performance even with large documents.

Next Steps

Editor Instance

Learn how to create and configure editor instances

Block System

Understand the block system and operations

Element System

Work with elements within blocks

Plugin System

Build custom plugins

Build docs developers (and LLMs) love