React или сайт без фреймворка: когда React оправдан
Когда сайту нужен React, а когда фреймворк только добавляет вес: замер размера сборки, сравнение трёх подходов, что браузер умеет сам, островки, одни и те же задачи двумя способами и частые ошибки.
Коротко
React оправдан, когда интерфейс работает как приложение: много состояний на одном экране, живые данные, сложные формы и редакторы — личные кабинеты, дашборды, SaaS, Mini Apps. Для сайтов, задача которых — показать содержание и привести к заявке, — корпоративных сайтов, лендингов, каталогов, блогов — фреймворк в основном добавляет вес: минимальное приложение на React — около 66 КБ сжатого JavaScript ещё до вашего кода, а браузер сегодня сам умеет модальные окна, всплывающие подсказки, проверку форм и переходы между страницами. Часто лучшее — сочетание: страницы — это серверный HTML, а React живёт только в тех блоках, где он действительно нужен.
Коротко: когда React, а когда без него
Задайте один вопрос: живёт ли экран после загрузки? Если человек в основном читает, сравнивает и заполняет форму — страница должна приходить готовой с сервера, а немного JavaScript добавит остальное. Если человек часами работает внутри экрана — фильтрует, перетаскивает, редактирует, получает живые обновления, — состояний становится много, и React держит их под контролем.
Ошибка — выбирать по привычке: лендинг на React, потому что «все так делают», или сложный дашборд на разрозненных скриптах, потому что «фреймворк тяжёлый». Обе ошибки стоят денег — первая скоростью и поиском, вторая ошибками и временем разработки.
- Экран читают — серверный HTML
- В экране работают — React
- Часто — оба на одном сайте
Сколько весит React: замер
Одна и та же кнопка со счётчиком, собранная Vite 8 в рабочем режиме. React 19.3, размеры файла JavaScript.
- React, после сжатия кода
- 214 КБ
- React, gzip при передаче
- 66 КБ — ещё до вашего собственного кода
- Чистый JavaScript, после сжатия
- 0,8 КБ
- Чистый JavaScript, gzip
- 0,5 КБ
- Что это значит
- На быстром интернете разница невелика; на слабом телефоне браузеру ещё нужно разобрать и выполнить этот код, прежде чем страница начнёт откликаться
React, React с серверной отрисовкой или без фреймворка
Три частых подхода рядом. React с серверной отрисовкой — это фреймворки вроде Next.js.
| Критерий | React в браузере | React + сервер (Next.js) | Серверный HTML без фреймворка |
|---|---|---|---|
| Первый экран | после загрузки и выполнения JavaScript | приходит готовым, потом оживает | приходит готовым |
| Содержание для поисковиков | только после JavaScript | в HTML | в HTML |
| Вес JavaScript | десятки КБ и больше | десятки КБ и больше | только то, что нужно странице |
| Сложные интерактивные экраны | сильная сторона | сильная сторона | сложнее, когда множатся состояния |
| Хостинг | любой статический | сервер на Node.js или платформа | любой, включая виртуальный |
| Зависимости для обновления | много | много, плюс фреймворк | мало |
| Проект через пять лет | требует регулярных обновлений | следует за изменениями фреймворка | работает как написан |
| Разработчиков на рынке | очень много | много | любой веб-разработчик |
| Лучше всего для | кабинетов, SaaS, Mini Apps | крупных продуктов с публичными страницами | сайтов, лендингов, каталогов, блогов |
Что браузер умеет без фреймворка сегодня
Многое, для чего раньше нужна была библиотека, теперь встроено в HTML и CSS — вместе с доступностью.
-
Модальные окна
<dialog>даёт затемнение фона, закрытие по Esc и удерживает фокус внутри. -
Всплывающие окна и меню
Атрибут
popoverоткрывает и закрывает их без единой строки JavaScript. -
Аккордеоны
<details>и<summary>— и содержание остаётся видимым для поисковиков. -
Проверка форм
required,type="email",patternи понятные сообщения от самого браузера. -
Переходы между страницами
View Transitions анимируют переход между страницами, как в приложении.
-
Адаптивные компоненты
Контейнерные запросы и
:has()подстраивают блок под его место без скриптов.
Какой подход под какой проект
Двенадцать типичных проектов с рекомендацией и причиной.
| Проект | Брать | Почему |
|---|---|---|
| Лендинг | без фреймворка | скорость на телефоне решает конверсию |
| Корпоративный сайт | без фреймворка | содержание, поиск и форма |
| Блог или медиа | без фреймворка | страницы читают, а не работают в них |
| Каталог с фильтрами | без фреймворка или островки | страницы для поиска, фильтры — небольшим скриптом |
| Интернет-магазин | островки | серверные страницы, React в корзине и оформлении, если они сложные |
| Калькулятор на сайте | островок | один интерактивный блок на обычной странице |
| Личный кабинет | React | много состояний и живые данные |
| Дашборд или админка | React | таблицы, фильтры и редактирование на одном экране |
| SaaS-продукт | React | интерфейс и есть продукт |
| Telegram Mini App | React | приложение внутри мессенджера |
| Редактор или конструктор | React | перетаскивание, отмена, живой предпросмотр |
| Крупный продукт с публичными страницами | Next.js | React везде, но страницы приходят готовыми для поиска |
Золотая середина: островки
Страница собирается на сервере обычным HTML — быстрая, видимая поисковикам, работающая без JavaScript. Там, где блоку действительно нужно жить, — калькулятор, конфигуратор, корзина, — React подключается только в этот блок. Остальная страница за него не платит.
Так же работают и инструменты полегче: Preact даёт подход React в несколько килобайт, htmx и Alpine.js добавляют поведение атрибутами, а Astro собирает из островков целые сайты. Выбор зависит от команды и от того, насколько вырастет интерактивная часть.
- Страница — HTML с сервера
- React — только там, где блок живёт
- Остальная страница не платит
Одни задачи на React и без него: 5 примеров
Модальное окно и поиск по списку двумя способами плюс островок. React-примеры проверены TypeScript 7 в строгом режиме, HTML-примеры — в Chromium.
Модальное окно: React
Состояние в компоненте; закрытие по Esc и удержание фокуса нужно писать отдельно.
import { useState } from 'react';
// React: состояние окна живёт в компоненте, а Esc и фокус — ваша забота
export function Подписаться() {
const [open, setOpen] = useState(false);
return (
<>
<button onClick={() => setOpen(true)}>Подписаться</button>
{open && (
<div role="dialog" aria-modal="true" className="modal">
<p>Оставьте почту — пишем раз в месяц.</p>
<button onClick={() => setOpen(false)}>Закрыть</button>
</div>
)}
</>
);
}
Модальное окно: без фреймворка
Работу делает <dialog>: затемнение, Esc и фокус даёт браузер.
<!-- без фреймворка: Esc, фокус и затемнение фона браузер берёт на себя -->
<button id="open">Подписаться</button>
<dialog id="subscribe">
<p>Оставьте почту — пишем раз в месяц.</p>
<form method="dialog"><button>Закрыть</button></form>
</dialog>
<script>
document.getElementById('open').addEventListener('click', () => {
document.getElementById('subscribe').showModal();
});
</script>
Поиск по списку: React
Список строится в браузере — пока JavaScript не выполнится, на странице ничего нет.
import { useState } from 'react';
const products = ['Дубовый стол', 'Дубовый стул', 'Сосновая полка', 'Ореховый стол'];
// React: список строит JavaScript в браузере
export function Search() {
const [query, setQuery] = useState('');
const found = products.filter((p) => p.toLowerCase().includes(query.toLowerCase()));
return (
<>
<input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Поиск" />
<ul>
{found.map((p) => (
<li key={p}>{p}</li>
))}
</ul>
</>
);
}
Поиск по списку: без фреймворка
Список приходит с сервера и виден сразу, а скрипт только прячет то, что не подходит.
<!-- список приходит с сервера: его видят поисковики, а JavaScript только скрывает лишнее -->
<input id="q" type="search" placeholder="Поиск">
<ul id="list">
<li>Дубовый стол</li>
<li>Дубовый стул</li>
<li>Сосновая полка</li>
<li>Ореховый стол</li>
</ul>
<script>
const items = [...document.querySelectorAll('#list li')];
document.getElementById('q').addEventListener('input', (e) => {
const q = e.target.value.toLowerCase();
for (const li of items) li.hidden = !li.textContent.toLowerCase().includes(q);
});
</script>
Островок: React в одном блоке
Страница остаётся серверным HTML, а React подключается только туда, где должен жить калькулятор.
import { createRoot } from 'react-dom/client';
import { Calculator } from './Calculator';
// страница — обычный серверный HTML, а React живёт в одном блоке
const mount = document.getElementById('calculator');
if (mount) {
createRoot(mount).render(<Calculator rate={Number(mount.dataset.rate)} />);
}
Частые ошибки при выборе
-
Лендинг как одностраничное приложение
Посетитель из рекламы ждёт JavaScript на слабом телефоне — и уходит до первого экрана.
-
Содержание только в браузере
Поисковики читают его хуже, а большинство ИИ-роботов вообще не выполняют JavaScript.
-
Дашборд на разрозненных скриптах
Без модели состояния каждая новая функция ломает две старые.
-
React ради одной кнопки
Десятки килобайт ради того, что
<dialog>илиpopoverделают сами. -
Выбирать по привычке команды
Инструмент выбирает задача, а не наоборот.
-
Забыть про обновления
Проект на фреймворке, брошенный на два года, требует переезда раньше любой новой функции.
Вопросы о React и сайтах без фреймворка
Нужен ли сайту React?
Сайту, который читают, — обычно нет. Интерфейсу, в котором работают, — обычно да.
Хуже ли сайт на React для SEO?
Если содержание строится только в браузере — да. С серверной отрисовкой (Next.js) содержание есть в HTML, и проблема уходит.
Сколько весит React?
Минимальное приложение на React 19, собранное Vite, — около 66 КБ в gzip до вашего кода; та же кнопка на чистом JavaScript — полкилобайта.
Сложнее ли поддерживать сайт без фреймворка?
Для контентных сайтов — наоборот: меньше зависимостей, меньше обновлений, и код прочитает любой веб-разработчик.
Можно ли добавить React потом?
Да, островками: новый интерактивный блок получает React без переписывания сайта.
Что такое Next.js?
Фреймворк для React, который собирает страницы на сервере, чтобы они приходили готовыми для людей и поисковиков.
А как же Vue или Svelte?
Логика та же: это инструменты для живых интерфейсов. Svelte компилируется в меньший JavaScript, но вопрос «живёт ли экран?» остаётся.
Не будет ли сайт без фреймворка выглядеть устаревшим?
Внешний вид от фреймворка не зависит: анимации, переходы и современная вёрстка делаются средствами CSS и самого браузера.
Форма
React или
свой код
Подбираю подход под задачу: React с TypeScript там, где интерфейс работает как приложение, и быстрый серверный HTML без фреймворка везде, где это не нужно. Расскажите о проекте — отвечу в течение рабочего дня.