webroad.online
  1. 1Web
  2. 2HTML
  3. 3CSS
  4. 4JavaScript
  5. 5TypeScript
  6. 6Git
  7. 7Tooling
  8. 8React
  9. 9State management
  10. 10Next.js
  11. 11Forms
  12. 12Data and backend
  13. 13SEO
  14. 14Tailwind CSS
  15. 15Animations
  16. 16Testing
  17. 17Architecture
State management · Lesson 4 of 4

RTK Query and the alternatives

Server state in Redux, tags, and a comparison with TanStack Query, SWR, Apollo, RSC.

Updated

What RTK Query is

RTK Query is the part of Redux Toolkit that handles server state: it fetches data from an API, keeps it in a cache, deduplicates requests, manages loading/error and refreshes automatically after mutations. It's the equivalent of TanStack Query, but built on top of Redux.

The idea: you no longer write useEffect + fetch + useState for loading + a reducer for the data. You describe the endpoints once, and RTK Query generates the hooks.

A complete API

// shared/api/base-api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  tagTypes: ['Post'],
  endpoints: build => ({
    getPosts: build.query<Post[], { page: number }>({
      query: ({ page }) => `posts?page=${page}`,
      providesTags: result => [
        ...(result ?? []).map(p => ({ type: 'Post' as const, id: p.id })),
        { type: 'Post', id: 'LIST' },
      ],
    }),
    addPost: build.mutation<Post, NewPost>({
      query: body => ({ url: 'posts', method: 'POST', body }),
      invalidatesTags: [{ type: 'Post', id: 'LIST' }],
    }),
  }),
})

export const { useGetPostsQuery, useAddPostMutation } = api
'use client'
const { data, isLoading, error } = useGetPostsQuery({ page })
const [addPost, { isLoading: saving }] = useAddPostMutation()

Tags — how it knows what to refresh

Concept Role
tagTypes the categories of data: 'Post', 'User'
providesTags a query declares what data it contains: { type: 'Post', id: 1 }
invalidatesTags a mutation declares what data it changed
the result queries with invalidated tags are refetched automatically

{ type: 'Post' } without an id invalidates every post; { type: 'Post', id: 1 } only post 1. It's the same principle as cacheTag / updateTag in Next and queryKey / invalidateQueries in TanStack Query.

The alternatives — a comparison

RTK Query TanStack Query SWR Apollo / urql Server Components
For REST / anything REST / anything REST / anything GraphQL anything, on the server
Depends on Redux nothing nothing GraphQL Next / RSC
Cache identified by endpoint + arguments, tags queryKey the key (URL) type + id (normalized) 'use cache' + cacheTag
Invalidation tags, declarative invalidateQueries mutate(key) automatic by type/id updateTag / revalidateTag
Size big (with Redux) medium small big 0 KB on the client
When you already have Redux the default on the client simple needs, Vercel-style a GraphQL API the default in Next for display

The practical recommendation:

  1. In the Next.js App Router, start with Server Components for displayed data and Server Actions for mutations.
  2. You need live data on the client (polling, infinite scroll, a dashboard) → TanStack Query.
  3. The project already uses Redux → RTK Query, so you don't end up with two systems.
  4. A GraphQL API → Apollo or urql.

Common mistakes

  • Copying a query's result into a Redux slice or into useState — two sources of truth.
  • Missing tags → after a mutation, the list doesn't update.
  • A single createApi per app; endpoints are added with injectEndpoints from each feature.

Summary

  • RTK Query = server state on top of Redux; endpoints → generated hooks.
  • providesTags + invalidatesTags = automatic refreshing after mutations.
  • Alternatives: TanStack Query (the default on the client), SWR (simple), Apollo (GraphQL), Server Components (Next).

Official sources

Exercises

Was this page helpful?

One tap — no account needed.