Собственный WYSIWYG-редактор мы разрабатываем, когда готовые инструменты не справляются с уникальными требованиями: нестандартные блоки, строгий выходной HTML, интеграция с бэкендом CMS. Наш опыт — 10+ лет и 50+ проектов — позволяет создавать редакторы под ключ за 3–12 недель. Мы гарантируем документацию, обучение и поддержку после сдачи.
Готовые редакторы (TinyMCE, CKEditor) часто имеют избыточную функциональность или, наоборот, не позволяют добавить специфические блоки: видео с кастомного плеера, интерактивные таблицы, встраивания из внешних сервисов. Кроме того, они генерируют грязный HTML, который сложно парсить на бэкенде. Кастомный редактор даёт полный контроль над моделью данных и выходным кодом.
Мы проектируем редактор под конкретную CMS: Laravel, WordPress, Django или кастомное решение. Контент храним в JSONB полях базы данных — это гибко и индексируемо. На выходе — быстрый, предсказуемый редактор с контекстным тулбаром, slash-командами и перетаскиванием блоков.
Сценарии для кастомного WYSIWYG-редактора
Свой редактор пишут, когда ни одно готовое решение не закрывает все требования. Типичные сценарии:
- Нестандартные блоки: кастомные галереи, интерактивные схемы, встраивание сторонних сервисов через iframe.
- Чистый HTML: нужен строгий выходной код без лишних обёрток, например, для дальнейшей конвертации в PDF или e-mail.
- Глубокая интеграция: редактор должен сохранять данные напрямую в CMS, работать с медиатекой, поддерживать права доступа.
- Производительность: при сотнях блоков на странице готовые редакторы тормозят — нужна виртуализация и ленивая загрузка.
Какой движок выбрать: ProseMirror, Slate или Lexical?
Писать редактор на голом contenteditable — путь к бесконечным багам. Выбор стоит между тремя зрелыми движками:
| Движок | Гибкость | Производительность | Поддержка React | Сложность освоения |
|---|---|---|---|---|
| ProseMirror | Высокая | Высокая | Через Tiptap | Высокая |
| Slate.js | Средняя | Средняя | Нативная | Средняя |
| Lexical | Высокая | Очень высокая | Нативная | Средняя |
ProseMirror даёт максимальный контроль над моделью данных — его используют, например, в New York Times. Slate.js лучше для React-проектов, где важна скорость разработки. Lexical от Meta — самый производительный, но сообщество ещё невелико.
Как построить модель данных для редактора?
Редактор должен работать с чёткой схемой. Два популярных подхода: Flat JSON (список блоков) и Дерево (вложенная структура).
Пример Flat JSON (стиль Editor.js):
{
"blocks": [
{
"id": "abc123",
"type": "header",
"data": {
"text": "Заголовок",
"level": 2
}
},
{
"id": "def456",
"type": "paragraph",
"data": {
"text": "Текст параграфа"
}
},
{
"id": "ghi789",
"type": "image",
"data": {
"url": "/uploads/photo.jpg",
"caption": "Подпись"
}
}
],
"version": "2.28.0"
}Пример дерева (ProseMirror/Tiptap):
{
"type": "doc",
"content": [
{
"type": "heading",
"attrs": {
"level": 2
},
"content": [
{
"type": "text",
"text": "Заголовок"
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Обычный "
},
{
"type": "text",
"marks": [
{
"type": "bold"
}
],
"text": "жирный"
},
{
"type": "text",
"text": " текст"
}
]
}
]
}В PostgreSQL данные хранятся в jsonb с GIN-индексом для полнотекстового поиска. Каждый тип блока — отдельная React-компонента с режимами просмотра и редактирования. Регистрируем блоки через плагинную систему:
interface BlockPlugin<T = Record<string, unknown>> {
type: string;
label: string;
icon: React.ReactNode;
defaultData: T;
render: (data: T, ctx: RenderContext) => React.ReactNode;
edit: (data: T, onChange: (data: T) => void) => React.ReactNode;
validate?: (data: T) => ValidationError[];
toHTML?: (data: T) => string;
} Контекстный тулбар, slash-команды и drag-and-drop
Тулбар появляется только при выделении текста — не занимает место и не отвлекает. Реализуем плавающую панель через FloatingToolbar с кнопками жирности, курсива, ссылки.
Slash-команды — стандарт для блочных редакторов: ввод / открывает меню выбора блока. Фильтрация по названию ускоряет работу.
Drag-and-drop сортировка блоков реализована через @dnd-kit/core. Блоки перетаскиваются без потери контента.
Как работает история изменений?
class EditorHistory {
private undoStack: EditorState[] = [];
private redoStack: EditorState[] = [];
private maxSize = 100;
push(state: EditorState) {
this.undoStack.push(structuredClone(state));
if (this.undoStack.length > this.maxSize) this.undoStack.shift();
this.redoStack = [];
}
undo(current: EditorState): EditorState | null {
if (this.undoStack.length === 0) return null;
this.redoStack.push(structuredClone(current));
return this.undoStack.pop()!;
}
redo(current: EditorState): EditorState | null {
if (this.redoStack.length === 0) return null;
this.undoStack.push(structuredClone(current));
return this.redoStack.pop()!;
}
}Для больших документов используем иммутабельные структуры (Immer) для экономии памяти.
Автосохранение — через дебаунс 2 секунды после последнего изменения. Статус отображается в интерфейсе: «Сохранено», «Сохранение...», «Есть изменения».
Рендеринг на фронтенде сайта
JSON редактора рендерится на публичной части сайта. Два подхода:
- SSR через React — данные передаются в те же компоненты блоков, что и в редакторе. Идеально для Next.js.
- Серверный рендеринг — на PHP или Node.js парсим JSON и генерируем HTML напрямую, без React.
| Подход | Производительность | Сложность | Гибкость |
|---|---|---|---|
| SSR с React | Средняя | Средняя | Высокая |
| Серверный рендеринг | Высокая | Высокая | Средняя |
Пример рендерера на Laravel:
class BlockRenderer {
protected array $renderers = [];
public function register(string $type, callable $renderer): void {
$this->renderers[$type] = $renderer;
}
public function render(array $blocks): string {
return collect($blocks)
->map(fn($block) => ($this->renderers[$block['type']] ?? fn() => '')($block['data']))
->implode("\n");
}
} Работа с медиа и производительность
Редактор интегрируется с медиатекой CMS. Изображения загружаются через drag-and-drop прямо в блок — прогресс-бар показывает статус. Для больших документов (сотни блоков и десятки изображений) включаем виртуализацию: рендерим только видимые блоки плюс буфер, остальные заменяем плейсхолдерами. Используем react-window или @tanstack/virtual.
Что входит в работу
- Анализ требований и проектирование модели данных
- Разработка ядра редактора и системы плагинов
- Реализация типовых и кастомных блоков
- Интеграция с CMS через REST API или прямую работу с БД
- Настройка автосохранения, истории, медиатеки
- Тестирование и отладка
- Создание документации и обучение редакторов
- Гарантийная поддержка 12 месяцев
Сроки и стоимость
Ориентировочные сроки:
- MVP: 3–4 недели
- Полноценный редактор: 8–12 недель
- Сложные проекты с уникальными блоками: до 16 недель
Стоимость рассчитывается индивидуально в зависимости от сложности. Конкретную цифру называем после аудита требований.
Почему выбирают нас?
- 10+ лет опыта в разработке редакторов для медиа и корпоративных сайтов
- 50+ проектов — от небольших блогов до крупных порталов
- Прозрачный процесс: каждые 2 недели демонстрируем промежуточные результаты
- Гарантия 12 месяцев и бесплатная поддержка после запуска
Закажите разработку редактора под ключ — свяжитесь с нами для консультации и получите инструмент, который полностью соответствует бизнес-задачам.







