DevHuzaifa's Portfolio
2 min readUpdated Aug 26, 2026

TanStack Query vs. SWR: Simplicity vs. Control in Modern React

Compare TanStack Query and SWR for React and Next.js. Explore code examples, mutations, bundle size, and find the right data-fetching tool for your stack.

#React#Next.js#Code Patterns

By Muhammad Huzaifa, Full-Stack Software Engineer

query vs swr

React Query vs. useSWR


When building scalable frontend applications in React and Next.js, managing server state inside useEffect quickly turns into an anti-pattern. Both TanStack Query (React Query) and Vercel's SWR solve this by automating background revalidation, cache invalidation, and request deduplication.

However, each library targets a distinct engineering tradeoff: SWR prioritizes minimal overhead and rapid velocity, while TanStack Query prioritizes fine-grained cache orchestration.

Basic Data Fetching: SWR vs. TanStack Query

SWR adopts a lightweight, URL-as-key paradigm:

typescript
import useSWR from 'swr';

const fetcher = (url: string) => fetch(url).then((res) => res.json());

function UserProfile({ userId }: { userId: string }) {
  const { data, error, isLoading } = useSWR(`/api/users/${userId}`, fetcher);

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Failed to load user.</p>;

  return <h1>{data.name}</h1>;
}

TanStack Query uses explicit structured array keys and a configuration object, giving you granular control over cache lifecycles.

typescript
import { useQuery } from '@tanstack/react-query';

const fetchUser = async (id: string) => {
  const res = await fetch(`/api/users/${id}`);
  if (!res.ok) throw new Error('Network error');
  return res.json();
};

function UserProfile({ userId }: { userId: string }) {
  const { data, error, isPending } = useQuery({
    queryKey: ['users', userId],
    queryFn: () => fetchUser(userId),
  });

  if (isPending) return <p>Loading...</p>;
  if (error) return <p>Failed to load user.</p>;

  return <h1>{data.name}</h1>;
}

Mutations and Cache Invalidation

SWR keeps mutations straightforward using its scoped mutate helper:

typescript
import useSWR, { useSWRConfig } from 'swr';

function UpdateName() {
  const { mutate } = useSWRConfig();

  const handleUpdate = async (newName: string) => {
    // Optimistic UI update, then trigger background revalidation
    mutate('/api/user', { name: newName }, false);
    await fetch('/api/user', {
      method: 'POST',
      body: JSON.stringify({ name: newName }),
    });
    mutate('/api/user');
  };

  return <button onClick={() => handleUpdate('Huzaifa')}>Update Name</button>;
}

TanStack Query offers a dedicated useMutation hook with complete lifecycle handlers (onMutate, onError, onSettled) for fine-grained rollbacks:

typescript
import { useMutation, useQueryClient } from '@tanstack/react-query';

function UpdateName() {
  const queryClient = useQueryClient();

  const mutation = useMutation({
    mutationFn: (newName: string) =>
      fetch('/api/user', {
        method: 'POST',
        body: JSON.stringify({ name: newName }),
      }),
    onSuccess: () => {
      // Invalidate all queries starting with the 'users' key
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });

  return (
    <button onClick={() => mutation.mutate('Huzaifa')}>Update Name</button>
  );
}

Key Architectural Differences

  • Bundle Footprint: SWR weighs ~4.5 kB, making it ideal for performance-sensitive client bundles. TanStack Query sits around ~13 kB due to its extensive feature surface.
  • Cache Organization: SWR works naturally with simple endpoints, whereas TanStack Query's array-based keys (['users', id, 'posts']) simplify invalidating related sub-trees of data.
  • Developer Experience: TanStack Query includes dedicated, official DevTools for inspecting queries and cache state in real time. SWR relies primarily on browser logging or third-party extensions.
  • Cross-Framework Support: SWR is tailored strictly for React/Next.js environments, while TanStack Query is framework-agnostic (React, Vue, Solid, Svelte).

Summary & Recommendation

In my engineering experience:

  • Choose SWR if you are building small-to-medium Next.js applications, want zero-friction caching, and prefer minimal cognitive overhead for standard REST/fetch workflows.
  • Choose TanStack Query if your application demands complex optimistic updates, deep cache invalidation hierarchies, offline synchronization, or multi-framework consistency.