Skip to main content

Overview

The Yoopta Editor is the main component that orchestrates all plugins, marks, and UI components. You create an editor instance with createYooptaEditor() (passing plugins and optional marks/value there), then render <YooptaEditor editor={editor} onChange={...} />. Content is stored on the editor instance and synced via onChange.

Creating an Editor Instance

Use createYooptaEditor() with an options object. Plugins are required; marks and initial value are optional:
import { createYooptaEditor } from '@yoopta/editor';
import { ParagraphPlugin } from '@yoopta/paragraph';

const editor = useMemo(
  () =>
    createYooptaEditor({
      plugins: [ParagraphPlugin],
      marks: [], // optional, e.g. from @yoopta/marks
      value: undefined, // optional initial content
      id: undefined, // optional editor ID
      readOnly: false,
    }),
  [],
);
Always wrap createYooptaEditor() in useMemo (with stable plugins/marks) to prevent recreating the editor on every render.
Plugins and marks are configured at creation time, not on the <YooptaEditor> component. Value is managed via the editor instance and onChange.

YooptaEditor Component Props

The <YooptaEditor> component accepts:

Required

editor
YooEditor
required
The editor instance from createYooptaEditor()

Optional

onChange
(value: YooptaContentValue, options: YooptaOnChangeOptions) => void
Called when content changes. Use it to sync value to your state (e.g. for saving or controlled usage).
onPathChange
(path: YooptaPath) => void
Called when the selection path changes (current block, selected blocks, etc.).
autoFocus
boolean
Whether to focus the editor on mount.
placeholder
string
Placeholder text when the editor is empty.
className
string
CSS class for the editor wrapper.
style
CSSProperties
Inline styles for the editor wrapper.
children
ReactNode
UI components to render inside the editor (e.g. toolbar, slash menu, block actions).
renderBlock
(props: RenderBlockProps) => ReactNode
Custom wrapper for each block (e.g. for drag-and-drop).

Basic Example

import { useMemo, useState } from 'react';
import { YooptaEditor, createYooptaEditor } from '@yoopta/editor';
import { ParagraphPlugin } from '@yoopta/paragraph';

const plugins = [ParagraphPlugin];

export default function MyEditor() {
  const editor = useMemo(
    () =>
      createYooptaEditor({
        plugins,
        value: undefined,
      }),
    [],
  );

  const [value, setValue] = useState(editor.children);

  const handleChange = (newValue, options) => {
    setValue(newValue);
    console.log('Operations:', options.operations);
  };

  return (
    <YooptaEditor
      editor={editor}
      onChange={handleChange}
      placeholder="Start typing..."
      autoFocus
    />
  );
}

Content Value Structure

Content is a record of block ID → block data. Type names use PascalCase for block types (e.g. Paragraph), and kebab-case for element types inside value.
type YooptaContentValue = Record<string, YooptaBlockData>;

type YooptaBlockData = {
  id: string;
  type: string; // e.g. 'Paragraph', 'HeadingOne'
  meta: {
    order: number;
    depth: number;
    align?: 'left' | 'center' | 'right';
  };
  value: SlateElement[]; // Slate nodes (paragraph, heading-one, etc.)
};

Example

{
  "block-1": {
    "id": "block-1",
    "type": "Paragraph",
    "meta": { "order": 0, "depth": 0 },
    "value": [
      {
        "id": "el-1",
        "type": "paragraph",
        "children": [{ "text": "Hello World!" }]
      }
    ]
  }
}

onChange Options

The onChange callback receives the new content and an options object:
type YooptaOnChangeOptions = {
  operations: YooptaOperation[]; // List of operations that caused the change
};
Use options.operations to know what changed (insert, update, delete, move, etc.) without diffing the whole value.

Styling the Editor

Using className

<YooptaEditor editor={editor} onChange={onChange} className="my-editor" />
.my-editor {
  max-width: 800px;
  margin: 0 auto;
  padding: 40px 20px;
  min-height: 500px;
}

Using style

<YooptaEditor
  editor={editor}
  onChange={onChange}
  style={{
    maxWidth: '800px',
    margin: '0 auto',
    padding: '40px 20px',
    minHeight: '500px',
  }}
/>

Multiple Editors

Each editor instance is independent. Create separate instances with createYooptaEditor() and separate state for each:
function MultipleEditors() {
  const editor1 = useMemo(() => createYooptaEditor({ plugins }), []);
  const editor2 = useMemo(() => createYooptaEditor({ plugins }), []);

  const [value1, setValue1] = useState(editor1.children);
  const [value2, setValue2] = useState(editor2.children);

  return (
    <>
      <YooptaEditor editor={editor1} onChange={setValue1} />
      <YooptaEditor editor={editor2} onChange={setValue2} />
    </>
  );
}

Next Steps