Последние полгода я собираю данные по использованию RSC в production — от небольших стартапов до enterprise-приложений. И всё чаще ловлю себя на мысли: мы применяем серверные компоненты как golden hammer, даже когда задача больше похожа на пластиковый конструктор.
Когда RSC действительно оправданы
В трёх сценариях серверные компоненты дают очевидные преимущества:
-
Контентные сайты с SEO-зависимостью
Рендеринг на сервере с встроенной streaming-загрузкой — это чистая магия для Time to First Byte. Но здесь RSC просто эволюция SSR, а не революция. -
Компоненты с тяжёлыми зависимостями
Пример из моей практики: PDF-рендерер, который тащит 4MB WASM. Выносим его в RSC — и клиент получает уже готовую разметку без скачивания неподъёмного кода. -
Админки с динамической конфигурацией
Если у вас интерфейс, где набор полей формы зависит от ACL, RSC позволяют собрать нужный UI до отправки клиенту.
// Пример: ACL-aware форма
async function AdminForm() {
const userPermissions = await fetchPermissions();
return (
<form>
{userPermissions.canEditName && <NameField />}
{userPermissions.canUpload && <FileUploader />}
</form>
);
}
Where RSC creates more problems than solutions
Проблемы начинаются, когда мы пытаемся впихнуть RSC в сценарии, где они не дают реальных преимуществ:
1. Auth-walled applications
Для закрытых SPA (типа внутренних дашбордов) SSR-преимущества RSC почти не работают. Но мы всё равно плодим серверные компоненты, потому что “это модерно”.
2. Mobile-like experiences
В мобильных приложениях важна predictive prefetching. Но Next.js с его <Link prefetch> иногда ведёт себя как overeager intern — начинает грузить данные для страницы, до которой пользователь, возможно, никогда не долистает.
3. Over-fetching под маской прогрессивной загрузки
Типичный антипаттерн:
// Плохо: компонент грузит всё сразу
async function UserProfile() {
const [user, posts, friends] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchFriends(),
]);
return <ProfilePage {...{user, posts, friends}} />;
}
// Лучше: разделяем на клиентские запросы
function UserProfile() {
const user = await fetchUser(); // RSC
return (
<ProfileLayout user={user}>
<ClientPosts userId={user.id} /> // CSR
<ClientFriends userId={user.id} /> // CSR
</ProfileLayout>
);
}
Архитектурная плата за RSC
Когда мы хвалимся уменьшением bundle size на клиенте, мы часто забываем о других издержках:
- Серверные ресурсы — теперь ваш Node.js должен рендерить React при каждом запросе, а не просто отдавать JSON
- Сложность дебага — трассировка ошибок теперь раскидана между сервером и клиентом
- Гибридная модель — приходится постоянно думать, где проходит граница между server и client components
So what’s the move?
Мой текущий подход:
- Начинать с традиционного CSR/SSR микса
- Вводить RSC только для конкретных тяжёлых компонентов
- Для auth-walled SPA использовать RSC минимально — только там, где есть реальные требования к первичной загрузке
Самый честный знак, что вы переусердствовали с RSC — когда вам приходится добавлять "use client" на половине файлов в проекте. Это не failure, а сигнал, что архитектура требует пересмотра.
P.S. Если вашему приложению не нужен SEO и вы не боретесь за каждый килобайт бандла, возможно, вам вообще не нужны RSC. И это окей.
Источник: https://www.reddit.com/r/reactjs/comments/1vf42mc/are_we_overusing_rsc_for_problems_that_dont/