SWR vs React Query: что выбрать для React-приложения в 2026
Сравнение SWR и React Query на практике: кэш, мутации, invalidation, SSR, производительность и критерии выбора для production и собеседования.
- Введение
- Короткий ответ
- Что именно сравниваем
- Архитектурная разница: простой revalidation против orchestration-слоя
- Как мыслит SWR
- Как мыслит React Query
- Где проходит реальная граница
- Таблица сравнения SWR и React Query
- Когда SWR обычно выигрывает
- 1. Контентные и dashboard-экраны с упором на чтение
- 2. Команде важен быстрый старт без тяжелого query runtime
- 3. Есть server-first или Next.js-ориентированная архитектура, а на клиенте нужен только легкий слой revalidation
- 4. Команда хочет меньше концепций
- Когда React Query обычно выигрывает
- 1. Приложение активно мутирует server state
- 2. В проекте много зависимых и пересекающихся запросов
- 3. Нужны devtools и трассировка поведения
- 4. Важна масштабируемость правил
- Мутации, invalidation и optimistic UI
- Ошибки в продакшене: где команды чаще ошибаются
- 1. Выбирают библиотеку по популярности, а не по характеру экрана
- 2. Копируют server state в Zustand или Redux "для удобства"
- 3. Слишком широкий ключ или слишком широкая invalidation
- 4. Маскируют архитектурную проблему большим staleTime
- 5. Путают производительность рендера с производительностью data layer
- Производительность: что измерять на практике
- Что обычно является узким местом
- Где SWR может быть выгоднее
- Где React Query может быть выгоднее
- Когда оптимизация преждевременна
- Практики, которые обычно работают лучше
- Частые ошибки
- Как отвечать на интервью про SWR vs React Query
- FAQ
- Можно ли начать с SWR, а потом перейти на React Query?
- Подходит ли SWR для production-приложения?
- React Query всегда требует больше кода?
- Что лучше для Next.js и server-first архитектуры?
- Какую ошибку интервьюеры видят чаще всего?
- Итоги
Введение
Запрос SWR vs React Query почти никогда не сводится к сравнению двух API. Команда обычно пытается ответить на более прикладной вопрос: какой слой взять для server state, чтобы не утонуть в ручном fetch, устаревшем UI (stale UI) и дублировании запросов в кэше. Если нужен более широкий контекст, сначала полезно посмотреть на React data fetching паттерны: там видно, что query-библиотека выбирается не в вакууме, а внутри общей архитектуры экрана.
Для React-проекта в 2026 году оба инструмента остаются актуальными, но решают задачу с разной степенью глубины. SWR делает ставку на простую модель чтения и revalidation. React Query, который сейчас развивается как TanStack Query, идет дальше: строит полноценный orchestration-слой для server state, кэша, мутаций и политики обновления.
Ниже разберем, где каждый подход выигрывает, где начинает мешать и как аргументировать выбор на техническом собеседовании.
Больше вопросов в Telegram
Ежедневные разборы и реальные кейсы с интервью
Короткий ответ
Если нужен быстрый ориентир без долгой теории, рабочее правило обычно такое:
- SWR берут для экранов с интенсивным чтением данных read-heavy экранов, где важны компактный API, revalidate-on-focus и понятная модель кэша без лишней церемонии.
- React Query берут для сложного production-приложения, где много мутаций, зависимых запросов, точечной invalidation, optimistic updates и длинного жизненного цикла server state.
- Если команда уже системно думает слоями кэша, то тему полезно изучать вместе с React caching стратегиями, потому что выбор между SWR и React Query почти всегда упирается именно в модель владения данными.
Это не значит, что SWR подходит только для игрушек, а React Query нужен только enterprise-командам. Речь про то, насколько оправдана сложность абстракции: чем сложнее ваш сценарий, тем сильнее окупается более строгий слой.
Что именно сравниваем
SWR изначально строился вокруг идеи stale-while-revalidate: сначала показываем доступные данные, затем тихо перепроверяем их актуальность. Для многих интерфейсов это очень естественная модель. Разработчик думает о ключе и fetcher, а библиотека берет на себя дедупликацию, фоновые перепроверки и базовую согласованность чтения.
React Query исторически пришел к более широкой задаче. Он не только читает данные, но и дает инфраструктуру для:
- query keys как контракта между экраном и кэшем;
- настройки stale/fresh политики;
- координации мутаций;
- точечной invalidation;
- optimistic updates;
- devtools и наблюдаемости;
- разделения query lifecycle по всему приложению.
Практический вывод простой: SWR ближе к аккуратному data fetching helper, React Query ближе к системному runtime для server state.
Архитектурная разница: простой revalidation против orchestration-слоя
Главное отличие заметно не в синтаксисе хука, а в том, как библиотека предлагает думать о системе.
Как мыслит SWR
В SWR основная единица работы — ключ ресурса. Вы описываете, как получить данные, и библиотека помогает:
- не дублировать одинаковые запросы;
- переиспользовать результат;
- перепроверять данные при возврате вкладки в фокус;
- локально обновлять кэш через
mutate.
Эта модель хороша там, где приложение в основном читает данные, а мутации либо редки, либо достаточно локальны.
import useSWR from "swr";
type User = {
id: string;
name: string;
email: string;
};
const fetcher = async (url: string): Promise<User> => {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
return response.json();
};
export function ProfileCard({ userId }: { userId: string }) {
const { data, error, isLoading, mutate } = useSWR(
`/api/users/${userId}`,
fetcher,
{
revalidateOnFocus: true,
dedupingInterval: 5_000,
}
);
if (isLoading) return <ProfileSkeleton />;
if (error) return <ErrorMessage text="Не удалось загрузить профиль" />;
if (!data) return null;
return (
<section>
<h3>{data.name}</h3>
<p>{data.email}</p>
<button onClick={() => mutate()}>Обновить</button>
</section>
);
}
Этот код легко читать. Для страницы профиля, каталога статей, панели с read-only статистикой или административного экрана без сложной клиентской координации SWR часто покрывает задачу без перегруза архитектуры.
Как мыслит React Query
React Query предлагает не просто хук для чтения, а управляемый query lifecycle. Это хорошо видно на экранах, где есть фильтры, pagination, мутации, зависимые панели и необходимость явно сказать, какие данные считать устаревшими.
import { keepPreviousData, useQuery } from "@tanstack/react-query";
type User = {
id: string;
name: string;
email: string;
};
async function getUser(userId: string): Promise<User> {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
return response.json();
}
export function ProfileCard({ userId }: { userId: string }) {
const userQuery = useQuery({
queryKey: ["user", userId],
queryFn: () => getUser(userId),
staleTime: 30_000,
gcTime: 10 * 60_000,
placeholderData: keepPreviousData,
});
if (userQuery.isPending) return <ProfileSkeleton />;
if (userQuery.isError) return <ErrorMessage text="Не удалось загрузить профиль" />;
return (
<section>
<h3>{userQuery.data.name}</h3>
<p>{userQuery.data.email}</p>
<button onClick={() => userQuery.refetch()}>Обновить</button>
</section>
);
}
Здесь видно больше решений по умолчанию: queryKey, staleTime, время жизни кэша, поведение placeholder-данных. На маленьком экране это может выглядеть тяжелее, но на большом приложении эта явность начинает экономить время команды.
Где проходит реальная граница
Если сформулировать совсем коротко:
- SWR удобнее, когда кэш можно мыслить как "ресурс с фоновым revalidation";
- React Query удобнее, когда кэш уже нельзя отделить от политики мутаций, инвалидации и координатора экрана.
Именно поэтому тема часто пересекается с полным разбором state management в React: server state быстро перестает быть просто "данными с сервера" и превращается в отдельный архитектурный слой.
Таблица сравнения SWR и React Query
| Критерий | SWR | React Query |
|---|---|---|
| Порог входа | Ниже, API компактнее | Выше, больше концепций |
| Базовый сценарий | Чтение данных и revalidation | Полный lifecycle server state |
| Мутации | Есть, но модель проще | Сильная сторона библиотеки |
| Invalidation | Более локальная и ручная | Точечная и системная |
| Optimistic updates | Возможны, но менее выразительны | Удобнее и богаче по сценариям |
| Настройка stale/fresh политики | Базовая и понятная | Гибкая и детализированная |
| Devtools и возможности мониторинга | Скромнее | Сильнее для командной разработки |
| Подходит для больших приложений | Да, но с оговорками | Обычно лучше масштабируется |
| Ментальная модель | Resource cache | Orchestration layer для server state |
| Лучший сценарий | Простые и средние read-heavy экраны | Data-heavy продукт с активными мутациями |
Из этой таблицы видно главное: вопрос не в том, какая библиотека "круче", а в том, сколько организационной нагрузки вы хотите и готовы взять прямо сейчас.
Прокачай React за 7 дней
20 вопросов и разборов по React Hooks
Когда SWR обычно выигрывает
SWR особенно хорош в четырех сценариях.
1. Контентные и dashboard-экраны с упором на чтение
Если экран в основном показывает данные, а не перестраивает их после каждой второй мутации, простая модель SWR часто оказывается самым дешевым решением. Она хорошо ложится на каталоги, профили, панели аналитики с редким обновлением и внутренние инструменты.
2. Команде важен быстрый старт без тяжелого query runtime
Не каждый проект существует достаточно долго, чтобы окупить внедрение мощного оркестрационного слоя. Иногда у команды 6-8 API-ресурсов, один CRUD-экран и понятный жизненный цикл. В такой ситуации React Query можно внедрить "на вырост", но этот «вырост» может так и не наступить.
3. Есть server-first или Next.js-ориентированная архитектура, а на клиенте нужен только легкий слой revalidation
Если критическое чтение уже вынесено на сервер, а на клиенте остались второстепенные части интерфейса, SWR часто хорошо работает как легкий мост между UI и сетью. Это особенно логично, когда основная сложность уже живет выше, а не в клиентском запросном слое.
4. Команда хочет меньше концепций
Иногда важен не максимум фич, а низкая когнитивная нагрузка. SWR в этом смысле выигрывает: меньше сущностей, меньше политики, меньше случаев, где junior-разработчик может случайно нарушить логику инвалидации данных на половине экрана.
Когда React Query обычно выигрывает
Есть и обратная картина, где React Query начинает заметно сильнее окупаться.
1. Приложение активно мутирует server state
Как только в системе много create/update/delete, проблема смещается с чтения на согласованность после записи. Здесь React Query удобнее, потому что дает более выразительный инструментарий для:
- отмены старых запросов;
- точечной invalidation;
- optimistic UI;
- rollback при ошибке;
- управления refetch после мутации.
import { useMutation, useQueryClient } from "@tanstack/react-query";
type Todo = {
id: string;
title: string;
done: boolean;
};
async function toggleTodo(todoId: string): Promise<Todo> {
const response = await fetch(`/api/todos/${todoId}/toggle`, {
method: "POST",
});
if (!response.ok) {
throw new Error(`Toggle failed: ${response.status}`);
}
return response.json();
}
export function useToggleTodo() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: toggleTodo,
onMutate: async (todoId: string) => {
await queryClient.cancelQueries({ queryKey: ["todos"] });
const previous = queryClient.getQueryData<Todo[]>(["todos"]);
queryClient.setQueryData<Todo[]>(["todos"], (items = []) =>
items.map((item) =>
item.id === todoId ? { ...item, done: !item.done } : item
)
);
return { previous };
},
onError: (_error, _todoId, context) => {
queryClient.setQueryData(["todos"], context?.previous);
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ["todos"] });
},
});
}
На экране задач или заказов правильная обработка мутаций может быть важнее, чем сам код загрузки списка. И именно здесь SWR чаще начинает требовать больше ручной дисциплины.
2. В проекте много зависимых и пересекающихся запросов
Если одна панель зависит от фильтра, другая — от результата первой, третья — от мутации из модального окна, вам нужен уже не просто кэш, а управляемый граф зависимостей. React Query с этим справляется увереннее.
3. Нужны devtools и трассировка поведения
На командном проекте вопрос "почему этот экран снова сходил в сеть?" возникает постоянно. Хорошие devtools здесь экономят часы отладки. Если рядом использовать React DevTools для дебага приложения, связка получается особенно полезной: одно показывает рендеры, другое — поведение query-слоя.
4. Важна масштабируемость правил
Когда в проекте 5 экранов, многие решения держатся в голове. Когда экранов 80, нужна система, а не набор договоренностей в чате. React Query обычно лучше выдерживает этот переход.
Мутации, invalidation и optimistic UI
Именно эта зона чаще всего решает исход выбора.
Для чтения данных SWR и React Query оба выглядят убедительно. Но production-приложение редко живет только чтением. Рано или поздно появляются:
- редактирование карточки сущности;
- переключение статуса в таблице;
- массовые операции;
- локальный optimistic UI;
- риск гонки между refetch и результатом мутации.
У SWR это тоже можно построить, но чаще через локальную дисциплину, ручной mutate и более аккуратную архитектуру кэша. У React Query эта часть ближе к основному сценарию использования.
Если формулировать честно: SWR решает задачу "поддерживать ресурс актуальным", React Query лучше решает задачу "управлять жизнью server state после чтения и записи".
Ошибки в продакшене: где команды чаще ошибаются
1. Выбирают библиотеку по популярности, а не по характеру экрана
Если проекту нужен легкий read-layer, React Query может оказаться лишним. Если продукт живет на сложных мутациях и связанной invalidation, SWR может быстро стать слишком ручным. Ошибка возникает не в коде, а в неправильной формулировке задачи.
2. Копируют server state в Zustand или Redux "для удобства"
После этого неважно, используете вы SWR или React Query. У вас уже два источника истины. Сначала запрос живет в query-кэше, потом его копируют в общий store, потом часть полей меняют локально, потом не понимают, какой слой должен объявить данные устаревшими. Эта проблема подробно пересекается с сравнением Redux vs Zustand vs Context в React: store и query cache решают разные задачи.
3. Слишком широкий ключ или слишком широкая invalidation
Если после любой мутации вы обновляете "все подряд", приложение теряет отзывчивость. Если ключи описаны небрежно, кэш перестает быть предсказуемым. На большом продукте это одна из первых причин сетевого шума.
4. Маскируют архитектурную проблему большим staleTime
Иногда команда замечает лишние запросы и просто увеличивает время свежести данных. Это может помочь, но может и спрятать stale UI. Хорошая политика кэша должна следовать за бизнес-смыслом данных, а не только за желанием "реже ходить в сеть".
5. Путают производительность рендера с производительностью data layer
Бывает, что вину возлагают на SWR или React Query, а проблема на самом деле в том, когда React перерисовывает компонент: слишком широкий provider, плохо разрезанные подписки, тяжелые derived-вычисления. Библиотека server state не исправит слабую структуру UI-дерева.
Производительность: что измерять на практике
Сравнивать SWR и React Query по абстрактному тезису "что быстрее" почти бессмысленно. Полезнее смотреть на конкретные сценарии.
Что обычно является узким местом
- число повторных запросов при навигации;
- поведение при возврате вкладки в фокус;
- стоимость refetch после мутации;
- количество ререндеров при смене фильтров;
- нагрузка на память из-за долгоживущего кэша;
- сетевой шум из-за слишком грубой инвалидации.
Где SWR может быть выгоднее
Если экран простой, а кэш работает как тонкий слой над ресурсом, SWR часто выигрывает меньшим runtime-overhead и более компактной ментальной моделью. На маленькой админке это может означать и более быстрый онбординг, и меньше кода поддержки.
Где React Query может быть выгоднее
Если экран сложный, React Query нередко показывает лучшую итоговую производительность не потому, что сам по себе "быстрее", а потому что дает команде лучшие инструменты избежать архитектурных ошибок: лишних refetch, двойных мутаций, дублирования запросов и грубой invalidation. В реальной жизни это важнее микроскопического сравнения хука.
Когда оптимизация преждевременна
Если у вас три запроса на весь экран и ни одной сложной мутации, не нужно заранее строить сложный query-рантайм. Но и обратная крайность вредна: если продукт уже живет на таблицах, фильтрах, optimistic UI и dependent queries, попытка сэкономить на слое данных почти всегда приводит к тому, что через два спринта разработчики вручную костылят логику, которую библиотека решала бы автоматически.
Для полной картины производительности эту тему полезно рассматривать вместе с React performance profiling и поиском узких мест. Тогда видно, где проблема в сети, где в кэше, а где в рендере.
Практики, которые обычно работают лучше
- Явно разделяйте UI state и server state. Ни SWR, ни React Query не должны превращаться в свалку локальных флагов интерфейса.
- Давайте ключам стабильную структуру. Ключ — это часть контракта, а не случайный массив строк.
- Проектируйте мутации вместе с политикой invalidation, а не после релиза.
- Выбирайте библиотеку по сложности жизненного цикла данных, а не по размеру команды или моде.
- Проверяйте поведение при фокусе вкладки, повторном заходе на экран и после неуспешной мутации.
- Если у вас server-first стек, не стоит переносить тяжелую клиентскую query-логику туда, где достаточно легкого слоя revalidation.
- Если экран строится через асинхронные границы, заранее продумывайте связку с Suspense для данных, а не прикручивайте ее поверх хаотичного fetch-слоя.
Частые ошибки
- Считать SWR и React Query взаимозаменяемыми только потому, что оба умеют кэшировать запросы.
- Использовать React Query как повод хранить в query cache то, что должно жить в локальном UI state.
- Ожидать, что SWR сам решит сложную оркестрацию мутаций на большом экране.
- Сравнивать библиотеки по демо-примеру
const { data } = ..., а не по реальному production-сценарию. - Говорить "React Query устарел, теперь есть только TanStack Query" как будто это другая библиотека. На практике это эволюция того же инструмента.
- Делать вывод о library fit без учета SSR, server-first архитектуры и реальной частоты мутаций.
Как отвечать на интервью про SWR vs React Query
Сильный ответ на собеседовании обычно звучит не как сравнение «кто круче», а как разбор компромиссов.
Рабочая формулировка может быть такой:
- SWR и React Query решают одну область задач, но с разной глубиной.
- SWR хорош как легкий слой для чтения данных и revalidation по модели stale-while-revalidate.
- React Query сильнее там, где server state активно мутируется и нужна точечная invalidation, optimistic updates и более строгий lifecycle.
- Для небольшого read-heavy интерфейса я бы чаще начал с SWR.
- Для крупного приложения с таблицами, фильтрами и сложной мутационной логикой я бы чаще выбрал React Query.
Если хотите поднять ответ до уверенного middle/senior-уровня, добавьте архитектурную мысль: я выбираю не «лучшую библиотеку вообще», а инструмент, чья сложность соответствует жизненному циклу данных на конкретном экране.
Практика реальных технических собеседований по React
Разберите SWR, React Query, caching, Suspense и архитектурные trade-off в формате mock interview с разбором ответов
FAQ
Можно ли начать с SWR, а потом перейти на React Query?
Да, если вы заранее не смешали query-слой с локальным состоянием интерфейса. Миграция легче проходит там, где ключи, fetcher-логика и границы server state уже оформлены аккуратно.
Подходит ли SWR для production-приложения?
Да. SWR не "облегченная игрушка", а зрелый инструмент. Ограничения начинаются не на слове production, а на сложности жизненного цикла данных: частые мутации, сложная invalidation и большой граф зависимых запросов.
React Query всегда требует больше кода?
Обычно да на простом экране, но не обязательно на сложном. Когда появляются мутации, rollback, отмена запросов и согласованность нескольких панелей, React Query часто экономит код за счет более явной модели.
Что лучше для Next.js и server-first архитектуры?
Зависит от того, сколько логики остается на клиенте. Если критические данные уже приходят с сервера, SWR может быть достаточно как легкий слой revalidation. Если на клиенте живет богатый lifecycle server state, React Query обычно удобнее.
Какую ошибку интервьюеры видят чаще всего?
Кандидат пересказывает API, но не объясняет, почему выбрал бы инструмент для конкретного сценария. Сильный ответ всегда привязан к типу экрана, частоте мутаций и модели invalidation.
Итоги
SWR vs React Query — это не спор "простого" и "продвинутого" инструмента в отрыве от контекста. Это выбор между двумя моделями работы с server state. SWR удобен там, где нужен легкий и понятный слой чтения с revalidation. React Query сильнее там, где данные активно живут после чтения: мутируются, инвалидируются, откатываются и координируются между несколькими частями интерфейса.
Если проект небольшой или преимущественно read-heavy, SWR часто дает лучший баланс простоты и пользы. Если продукт насыщен мутациями, связанными запросами и долгоживущим кэшем, React Query обычно окупает свою сложность быстрее, чем кажется в начале.
Больше вопросов в Telegram
Ежедневные разборы и реальные кейсы с интервью
Автор
Lexicon Team
Читайте также
frontend
React data fetching паттерны: как выбрать подход без waterfalls и stale UI
Разбираем React data fetching паттерны: useEffect, query-библиотеки, route loaders, Suspense и server-first подход. Где какой паттерн уместен, какие trade-off важны и что отвечать на собеседовании.
frontend
React caching стратегии: как выбрать слой кэша и не сломать данные
Разбираем React caching стратегии: memoization, query cache, HTTP cache, SSR/RSC cache, invalidation, performance trade-off и типичные production-ошибки.
frontend
React Suspense для данных: как загружать данные без хаоса в loading-state
Подробно разбираем React Suspense для данных: как работает throw promise, где проходят границы ответственности, как сочетать Suspense с кэшем, Error Boundary и что отвечать на собеседовании.