Собственный 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 месяцев и бесплатная поддержка после запуска
Закажите разработку редактора под ключ — свяжитесь с нами для консультации и получите инструмент, который полностью соответствует бизнес-задачам.







