Skip to content

v6.0.0

Choose a tag to compare

@Darginec05 Darginec05 released this 24 Feb 22:23
· 32 commits to master since this release

Yoopta Editor v6.0.0

Release Date: February 23, 2026

This is a major release that transforms Yoopta into a fully headless, theme-ready editor with a new plugin architecture, real-time collaboration, and modern tooling.


Highlights

  • Headless architecture — Plugins define structure and behavior only; UI is optional and provided by theme packages
  • New UI layer (@yoopta/ui) — Modular, composable UI components rendered as children of <YooptaEditor>
  • Theme system@yoopta/themes-shadcn (production-ready), @yoopta/themes-material (in progress), @yoopta/themes-base
  • Real-time collaboration — New @yoopta/collaboration package powered by Yjs CRDT
  • Namespace APIsBlocks.*, Elements.*, Marks.*, Selection.* for structured editor operations
  • Element buildereditor.y() API for programmatic content creation
  • New plugins — Steps, Tabs, Carousel, Emoji, Mention
  • Email serializereditor.getEmail() for email-compatible HTML output
  • Turborepo + Yarn Berry — Modern monorepo tooling with build caching and selective dev mode

Breaking Changes

Editor creation

Plugins and marks are now passed to createYooptaEditor() instead of the <YooptaEditor> component:

// Before (v4/v5)
<YooptaEditor plugins={plugins} marks={marks} value={value} onChange={onChange} />

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

<YooptaEditor editor={editor}>
  <YooptaToolbar />
  <YooptaSlashCommandMenu />
  {/* ... */}
</YooptaEditor>

Content management

The value/onChange controlled pattern on the component is removed:

// Set content
editor.setEditorValue(value);

// Listen to changes
editor.on('change', (value) => { ... });

// Or use onChange prop on <YooptaEditor>
<YooptaEditor editor={editor} onChange={(value, { operations }) => { ... }} />

UI components

@yoopta/tools is deprecated. Migrate to @yoopta/ui:

import {
  YooptaToolbar,
  YooptaActionMenuList,
  YooptaSlashCommandMenu,
  YooptaFloatingBlockActions,
  SelectionBox,
} from '@yoopta/ui';

All UI elements are now children of <YooptaEditor> and use useYooptaEditor() internally.


New Features

Headless plugin architecture

Plugins are now purely structural — they define block types, elements, behavior, and parsers without shipping any visual UI. Theme packages provide optional styled renderers:

// Apply theme to all plugins
import { applyTheme } from '@yoopta/themes-shadcn';
const plugins = applyTheme([Paragraph, Callout, Headings.HeadingOne, ...]);

// Or apply to individual plugins
import { CalloutUI } from '@yoopta/themes-shadcn/callout';
const CalloutWithUI = Callout.extend({ elements: CalloutUI });

Namespace APIs

Structured APIs for all editor operations:

import { Blocks, Elements, Marks, Selection } from '@yoopta/editor';

Blocks.insertBlock(editor, { ... });
Blocks.deleteBlock(editor, { ... });
Blocks.toggleBlock(editor, { ... });
Blocks.moveBlock(editor, { ... });
Blocks.duplicateBlock(editor, { ... });
Blocks.splitBlock(editor, { ... });
Blocks.mergeBlock(editor, { ... });
Blocks.increaseBlockDepth(editor, { ... });
Blocks.decreaseBlockDepth(editor, { ... });

Elements.insertElement(editor, { ... });
Elements.updateElement(editor, { ... });
Elements.deleteElement(editor, { ... });
Elements.getElement(editor, { ... });

Marks.update(editor, { type: 'highlight', value: { color: 'red' }, at: [0, 1, 2] });

Element builder (editor.y)

Programmatic content creation:

// Create block element
editor.y('paragraph', { props: {...}, children: [...] });

// Create text node with marks
editor.y.text('Hello', { bold: true, italic: true });

// Create inline element
editor.y.inline('link', { props: { url: '...' }, children: [...] });

Event system

editor.on('change', (value) => { ... });
editor.on('focus', () => { ... });
editor.on('blur', () => { ... });
editor.on('block:copy', (data) => { ... });
editor.on('path-change', (paths) => { ... });
editor.once('change', handler);
editor.off('change', handler);

ElementOptions (@yoopta/ui)

Compound component for inline element configuration popovers, built on Radix UI:

<ElementOptions.Root blockId={blockId} element={element}>
  <ElementOptions.Trigger />
  <ElementOptions.Content>
    <ElementOptions.Group label="Settings">
      <ElementOptions.Select options={options} />
      <ElementOptions.Toggle />
      <ElementOptions.Slider />
      <ElementOptions.ColorPicker />
      <ElementOptions.Input />
    </ElementOptions.Group>
  </ElementOptions.Content>
</ElementOptions.Root>

Hooks: useElementOptions() and useUpdateElementProps<T>().

Real-time collaboration (@yoopta/collaboration)

New core package for collaborative editing:

  • Technology: Yjs CRDT for conflict-free sync with Slate-Yjs binding
  • Features: Awareness, remote cursors, selection tracking
  • Infrastructure: WebSocket provider support
  • Hooks: useCollaboration, useConnectionStatus, useRemoteCursors
import { useCollaboration } from '@yoopta/collaboration';

Email serializer

const emailHTML = editor.getEmail(content, {
  head: { title: 'My Email' },
  body: { attrs: { style: { width: '600px' } } },
  customTemplate: (content) => `<html>...${content}...</html>`,
});

All 23 plugins include email parsers with table-based layouts for email client compatibility.

New plugins

  • Steps — Numbered step-by-step lists with heading and content per step
  • Tabs — Tabbed content containers (renders as sections in email)
  • Carousel — Horizontally scrollable content cards
  • Emoji — Emoji insertion with dropdown picker
  • Mention — @mention with dropdown, avatar support, floating position, extend render

Table plugin enhancements

  • scrollable prop (default: true) — tables respect column widths and scroll horizontally
  • Column resize handles with portal rendering
  • Improved column/row controls positioning in scrollable tables

JSON clipboard support

Copy/paste preserves Yoopta block structure via JSON clipboard data.


Bug Fixes

Editor core

  • Block focus on click — Fixed brief focus jump to the first block when clicking other blocks
  • SelectionBox scroll — Auto-scrolling when dragging selections near viewport edges; selection origin tracks document coordinates
  • SelectionBox — Eliminated native text selection during programmatic block selection

Lists

  • Enter key — Fixed focus loss when pressing Enter in bulleted, numbered, and todo lists (cursor became invisible, especially in Safari)

Image

  • Inline toolbar after upload — Fixed toolbar not appearing after file upload (file picker dialog cleared Slate selection)
  • Left-side resize — Fixed left handle resizing with proper position anchoring
  • Resize handlers — Added white ring styling for visibility on dark images
  • Preview blob leak — Fixed stale closure in useImagePreview where clearPreview() failed to revoke blob URLs
  • Upload validation — Enhanced hooks with validation for upload and delete options

Video

  • Preview blob leak — Fixed same stale closure issue as image plugin in useVideoPreview

FloatingBlockActions

  • Hover detection — Improved reach detection using JavaScript-based extended bounds instead of CSS pseudo-elements
  • Margin offset — Fixed positioning when blocks have margin-top styles

FloatingToolbar

  • Safari — Fixed immediate toolbar closure on button clicks (preventDefault on mouseDown)
  • Safari inline toolbars — Fixed incorrect (0,0) positioning using useLayoutEffect and isReady state

HighlightColorPicker

  • Click-outside handling — Fixed using FloatingOverlay for cross-browser reliability
  • History flooding — Added 300ms debounce to onChange to prevent excessive undo/redo entries

Steps plugin

  • moveUp/moveDown — Fixed buggy methods using proper Transforms.moveNodes

Code/CodeGroup

  • Theme isolation — Shiki theme colors now use scoped CSS variables on elements instead of global document variables

Infrastructure

Turborepo + Yarn Berry migration

  • From: Lerna + Yarn v1
  • To: Turborepo + Yarn Berry (v4)
  • workspace:* protocol resolves local packages correctly
  • Build caching and filtering for selective dev mode
  • Eliminated packages/development/web/next-app-example/ is now the dev workspace

New commands

yarn dev                                    # Start dev server
yarn dev --filter=@yoopta/editor            # Dev + watch one package
yarn dev --filter=@yoopta/editor --filter=@yoopta/paragraph  # Watch multiple
yarn build --filter=@yoopta/editor          # Build single package

Publishing

Versioning uses Changesets with fixed version groups — all @yoopta/* packages share the same version.

React 19

Monorepo upgraded to React 19 while maintaining backward compatibility via peerDependencies: "react": ">=18.2.0".


Migration Guide

1. Update packages

npm install @yoopta/editor@^6.0.0 @yoopta/ui@^6.0.0
# Install theme if desired
npm install @yoopta/themes-shadcn@^6.0.0

2. Update editor creation

// Old
const editor = useMemo(() => createYooptaEditor(), []);
<YooptaEditor editor={editor} plugins={plugins} marks={marks} value={value} onChange={onChange} />

// New
const editor = useMemo(() => createYooptaEditor({ plugins, marks, value: initialValue }), []);
<YooptaEditor editor={editor} onChange={(value, { operations }) => { ... }}>
  {/* UI components as children */}
</YooptaEditor>

3. Replace @yoopta/tools with @yoopta/ui

import {
  YooptaToolbar,
  YooptaActionMenuList,
  YooptaSlashCommandMenu,
  YooptaFloatingBlockActions,
} from '@yoopta/ui';

4. Apply theme (optional)

import { applyTheme } from '@yoopta/themes-shadcn';
const plugins = applyTheme([Paragraph, Callout, ...]);

5. Content handling

// Set value
editor.setEditorValue(savedContent);

// Get value
const content = editor.getEditorValue();

Package Versions

All @yoopta/* packages are published at version 6.0.0:

Package Description
@yoopta/editor Core editor engine
@yoopta/ui UI components (toolbar, menus, block actions, element options, selection box)
@yoopta/collaboration Real-time collaboration (Yjs)
@yoopta/exports HTML/Markdown/PlainText/Email serializers
@yoopta/marks Text formatting (Bold, Italic, Underline, Strike, Code, Highlight)
@yoopta/themes-shadcn Shadcn UI theme
@yoopta/themes-material Material Design theme (in progress)
@yoopta/themes-base Base theme
@yoopta/paragraph Paragraph plugin
@yoopta/headings HeadingOne, HeadingTwo, HeadingThree
@yoopta/lists BulletedList, NumberedList, TodoList
@yoopta/blockquote Blockquote plugin
@yoopta/callout Callout plugin
@yoopta/code Code block plugin
@yoopta/image Image plugin
@yoopta/video Video plugin
@yoopta/embed Embed plugin
@yoopta/file File plugin
@yoopta/link Link plugin
@yoopta/divider Divider plugin
@yoopta/table Table plugin
@yoopta/accordion Accordion plugin
@yoopta/mention Mention plugin
@yoopta/emoji Emoji plugin
@yoopta/steps Steps plugin
@yoopta/tabs Tabs plugin
@yoopta/carousel Carousel plugin
@yoopta/table-of-contents Table of Contents plugin