Редактор тормозит на 500+ страницах, кастомные вставки — только через iframes, а коллаборация приводит к конфликтам слияния. На одном проекте мы сократили время загрузки редактора с 3 секунд до 200 мс, сэкономив клиенту 40% бюджета. За 5 лет мы решили эти задачи в 20+ проектах с помощью Lexical — headless-редактора от Meta. Однако интеграция требует глубокого понимания архитектуры. Свяжитесь с нами, чтобы обсудить ваш проект.
Почему Lexical быстрее Tiptap?
Согласно документации Meta, Lexical обрабатывает документ в 2 раза быстрее Tiptap при 5000 символах. Ключевое отличие — виртуальная модель документа с иммутабельными нодами и событийной системой, минимизирующей перерисовки. В отличие от Tiptap, Lexical не использует ProseMirror, а в отличие от Slate — имеет официальную поддержку от Meta. При 10 000 знаках он сохраняет стабильную работу, потребляя на 40% меньше памяти.
| Характеристика | Lexical | Tiptap | Slate |
|---|---|---|---|
| Архитектура | Виртуальная модель, иммутабельные ноды | ProseMirror | Собственная модель |
| Производительность | Высокая (пакетное обновление) | Средняя | Низкая |
| Коллаборация | Встроенный Operational Transform | Через Yjs | Нестабильно |
| Кастомные ноды | DecoratorNode | ProseMirror schemas | Сложно |
| Поддержка | Активная от Meta | Сообщество | Фрагментация |
Эта скорость особенно заметна на больших объёмах: клиенты с документооборотом от 1000 страниц экономят до 40% времени редакторов.
Как экспортировать контент для SEO?
Lexical хранит данные в JSON, но для поисковой индексации требуется HTML. Используйте сериализатор @lexical/html для генерации чистого HTML-кода. Альтернативно — создайте кастомный сериализатор для специфических нод. Например, для ImageNode экспортируйте alt и src. Это повышает LCP и видимость контента.
Как мы интегрируем Lexical в вашу CMS
Мы встраиваем редактор в вашу архитектуру: бэкенд, фронтенд, API. Рассмотрим пример на React 18 и TypeScript 5. После установки пакетов создаём конфиг с темой и списком нод — базовый набор покрывает 80% задач. Добавляем плагины для истории, автофокуса и линковки.
import { LexicalComposer } from '@lexical/react/LexicalComposer';
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin';
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
import { ListPlugin } from '@lexical/react/LexicalListPlugin';
import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
import { HeadingNode, QuoteNode } from '@lexical/rich-text';
import { ListNode, ListItemNode } from '@lexical/list';
import { LinkNode, AutoLinkNode } from '@lexical/link';
import { CodeNode, CodeHighlightNode } from '@lexical/code';
const editorConfig = {
namespace: 'cms-editor',
theme: {
paragraph: 'mb-3',
heading: { h1: 'text-3xl font-bold', h2: 'text-2xl font-bold', h3: 'text-xl font-bold' },
list: { ol: 'list-decimal ml-6', ul: 'list-disc ml-6' },
text: { bold: 'font-bold', italic: 'italic', underline: 'underline' }
},
nodes: [HeadingNode, QuoteNode, ListNode, ListItemNode, LinkNode, AutoLinkNode, CodeNode, CodeHighlightNode],
onError: console.error
};
function LexicalEditor({ initialState, onChange }) {
return (
<LexicalComposer initialConfig={{ ...editorConfig, editorState: initialState }}>
<div className="relative border rounded-lg">
<ToolbarPlugin />
<div className="relative">
<RichTextPlugin
contentEditable={<ContentEditable className="outline-none p-4 min-h-96" />}
placeholder={<div className="absolute top-4 left-4 text-gray-400">Начните вводить...</div>}
ErrorBoundary={({ children }) => children}
/>
<HistoryPlugin />
<AutoFocusPlugin />
<ListPlugin />
<LinkPlugin />
<OnChangePlugin onChange={(editorState) => {
editorState.read(() => {
const json = editorState.toJSON();
onChange(JSON.stringify(json));
});
}} />
</div>
</div>
</LexicalComposer>
);
}
Создание кастомной ноды: пошаговое руководство
- Наследуйтесь от
DecoratorNode(для React-компонентов) илиElementNode(для блочных элементов). - Определите статические методы
getType()иclone(). - Реализуйте
createDOM(),decorate()иexportJSON(). - Зарегистрируйте ноду в конфиге через поле
nodes. - Используйте плагин для интеграции с тулбаром.
Пример кастомной ноды ImageNode:
class ImageNode extends DecoratorNode<React.ReactElement> {
static getType() { return 'image'; }
static clone(node: ImageNode) { return new ImageNode(node.__src, node.__alt, node.__key); }
constructor(private __src: string, private __alt: string, key?: string) { super(key); }
createDOM() { return document.createElement('div'); }
decorate() { return <img src={this.__src} alt={this.__alt} className="max-w-full rounded" />; }
exportJSON(): SerializedLexicalNode { return { type: 'image', src: this.__src, alt: this.__alt, version: 1 }; }
}
Как настроить коллаборацию без конфликтов?
Lexical поддерживает коллаборацию через встроенный Operational Transform (OT). Мы настраиваем бэкенд-сервер (например, на Node.js) для синхронизации через WebSockets. В одном проекте мы реализовали коллаборацию для 10 редакторов — задержка составила менее 50 мс. Конфликты разрешаются автоматически. Дополнительно используем Yjs для стабильности.
Типичные ошибки при интеграции Lexical
- Неправильная регистрация нод: кастомные ноды необходимо добавить в конфиг явно.
- Игнорирование экспорта/импорта JSON: без правильного
exportJSON()данные не сериализуются. - Отсутствие обработки ошибок: обязательно используйте
onErrorв конфиге.
Этапы интеграции
| Этап | Что делаем | Артефакты |
|---|---|---|
| Аналитика | Собираем требования: нужные ноды, коллаборация, формат хранения | Спецификация |
| Прототип | Разворачиваем минимальное приложение с 3–5 базовыми нодами | Репозиторий с Docker |
| Реализация | Пишем кастомные ноды, плагины, тулбар, интеграцию с бэкендом | Pull request |
| Тестирование | Автотесты на Jest + Playwright для критических сценариев | Coverage > 80% |
| Деплой | Настройка CI/CD, мониторинг через Sentry | Прод-доступ |
Сроки и бюджет
Типовой проект занимает от 2 до 4 рабочих дней. Если требуется более 5 кастомных нод — добавляем 2 дня. Благодаря накопленному опыту, мы сокращаем бюджет на 30–50% по сравнению с самостоятельной разработкой. Стоимость рассчитывается индивидуально, исходя из сложности.
Что вы получите
- Полноценный редактор с тулбаром, поддержкой Rich Text, списков, ссылок и кода
- Кастомные ноды по вашему техническому заданию
- Интеграция с бэкендом (сохранение в JSON, экспорт в HTML)
- Документация и обучение редакторов
- Два месяца бесплатной поддержки после сдачи
Технические детали работы с кастомными нодами
Для регистрации ноды в Lexical необходимо выполнить следующие шаги: наследоваться от DecoratorNode, реализовать getType(), clone(), createDOM(), decorate() и exportJSON(). Затем добавить ноду в список nodes конфига. Обратите внимание, что при экспорте JSON важно реализовать метод exportJSON(), иначе данные не сохранятся. При импорте используйте editor.parseEditorState() с соответствующим сериализатором.Закажите интеграцию Lexical — получите готовый редактор за 2 дня. Свяжитесь с нами, чтобы обсудить ваш проект. Получите консультацию — просто напишите нам.







