Web Development

React Query vs SWR vs TanStack: Ultimate 2025 Guide

Compare React Query, SWR, and TanStack for data fetching in 2025. Expert analysis with code examples to help you choose the right solution for your React app.

· By PropTechUSA AI
11m
Read Time
2.1k
Words
5
Sections
11
Code Examples

The landscape of React data fetching libraries has evolved dramatically, with developers now having powerful options that go far beyond basic fetch() calls. As React applications become more complex—especially in PropTech where real-time property data, user interactions, and complex state management are crucial—choosing the right data fetching solution can make or break your application's performance and developer experience.

The Evolution of React Data Fetching Libraries

Data fetching in React has transformed from simple component-level API calls to sophisticated state management solutions. The emergence of libraries like React Query, SWR, and TanStack Query has revolutionized how we handle server state, caching, and synchronization.

Why Traditional Approaches Fall Short

Traditional data fetching approaches using useEffect and useState create several challenges:

  • Manual cache management leading to inconsistent data
  • No automatic background refetching or synchronization
  • Complex loading and error state management
  • Duplicated requests across components
  • Poor offline experience and stale data handling

In PropTech applications, where property listings, pricing data, and user preferences need constant synchronization, these limitations become critical bottlenecks.

The Modern Data Fetching Paradigm

Modern libraries treat server state as a first-class citizen, distinct from client state. They provide:

  • Intelligent caching strategies
  • Automatic background synchronization
  • Optimistic updates and rollback mechanisms
  • Built-in loading, error, and success states
  • Request deduplication and batching

Understanding the Core Players

React Query (Legacy) vs TanStack Query

React Query was rebranded to TanStack Query in 2022, expanding beyond React to support Vue, Svelte, and other frameworks. While "React Query" often refers to TanStack Query's React adapter, understanding this distinction is crucial for 2025.

typescript
// TanStack Query v5(latest) import { useQuery } from '@tanstack/react-query' class="kw">const PropertyQuery = () => {

class="kw">const { data, isLoading, error } = useQuery({

queryKey: ['properties', filters],

queryFn: () => fetchProperties(filters),

staleTime: 5 60 1000, // 5 minutes

})

class="kw">return (

<div>

{isLoading && <PropertySkeleton />}

{error && <ErrorBoundary error={error} />}

{data && <PropertyList properties={data} />}

</div>

)

}

SWR: Stale-While-Revalidate Philosophy

SWR, developed by Vercel, implements the stale-while-revalidate HTTP caching strategy. It's lightweight, focused, and integrates seamlessly with Next.js applications.

typescript
import useSWR from &#039;swr&#039; class="kw">const PropertyDetails = ({ propertyId }: { propertyId: string }) => {

class="kw">const { data, error, mutate } = useSWR(

/api/properties/${propertyId},

fetcher,

{

revalidateOnFocus: true,

revalidateOnReconnect: true,

}

)

class="kw">const updateProperty = class="kw">async (updates: PropertyUpdates) => {

// Optimistic update

mutate({ ...data, ...updates }, false)

class="kw">await updatePropertyAPI(propertyId, updates)

mutate() // Revalidate

}

class="kw">return <PropertyCard data={data} onUpdate={updateProperty} />

}

Feature Comparison Matrix

| Feature | TanStack Query | SWR | React Query v3 |

|---------|----------------|-----|-----------------|

| Bundle Size | ~39kb | ~4.2kb | ~36kb |

| TypeScript Support | Excellent | Good | Good |

| DevTools | Advanced | Basic | Advanced |

| Infinite Queries | Built-in | Manual | Built-in |

| Optimistic Updates | Advanced | Good | Advanced |

| Offline Support | Excellent | Basic | Good |

| Background Refetching | Configurable | Automatic | Configurable |

| Request Deduplication | Yes | Yes | Yes |

Implementation Deep Dive

Setting Up Your Data Layer

When building PropTech applications, establishing a robust data layer is essential. Here's how each library approaches setup:

#### TanStack Query Setup

typescript
// queryClient.ts import { QueryClient } from &#039;@tanstack/react-query&#039; export class="kw">const queryClient = new QueryClient({

defaultOptions: {

queries: {

staleTime: 1000 60 5, // 5 minutes

gcTime: 1000 60 30, // 30 minutes(formerly cacheTime)

retry: (failureCount, error) => {

class="kw">if (error.status === 404) class="kw">return false

class="kw">return failureCount < 3

},

},

},

})

// App.tsx import { QueryClientProvider } from &#039;@tanstack/react-query&#039; import { ReactQueryDevtools } from &#039;@tanstack/react-query-devtools&#039; class="kw">function App() {

class="kw">return (

<QueryClientProvider client={queryClient}>

<PropertyApp />

<ReactQueryDevtools initialIsOpen={false} />

</QueryClientProvider>

)

}

#### SWR Configuration

typescript
// _app.tsx(Next.js) import { SWRConfig } from &#039;swr&#039; import axios from &#039;axios&#039; class="kw">const fetcher = (url: string) => axios.get(url).then(res => res.data) class="kw">function MyApp({ Component, pageProps }) {

class="kw">return (

<SWRConfig

value={{

fetcher,

revalidateOnFocus: false,

errorRetryCount: 3,

errorRetryInterval: 5000,

dedupingInterval: 2000,

}}

>

<Component {...pageProps} />

</SWRConfig>

)

}

Advanced Patterns and Real-World Examples

#### Infinite Queries for Property Listings

Propertytech applications often require infinite scrolling for large datasets:

typescript
// TanStack Query infinite query import { useInfiniteQuery } from &#039;@tanstack/react-query&#039; class="kw">const useInfiniteProperties = (filters: PropertyFilters) => {

class="kw">return useInfiniteQuery({

queryKey: [&#039;properties&#039;, &#039;infinite&#039;, filters],

queryFn: ({ pageParam = 0 }) =>

fetchProperties({ ...filters, page: pageParam }),

getNextPageParam: (lastPage, pages) =>

lastPage.hasNext ? pages.length : undefined,

initialPageParam: 0,

})

}

class="kw">const PropertyListContainer = () => {

class="kw">const {

data,

fetchNextPage,

hasNextPage,

isFetchingNextPage

} = useInfiniteProperties(filters)

class="kw">return (

<InfiniteScroll

dataLength={data?.pages.flatMap(p => p.properties).length ?? 0}

next={fetchNextPage}

hasMore={hasNextPage}

loader={<PropertySkeleton />}

>

{data?.pages.map(page =>

page.properties.map(property =>

<PropertyCard key={property.id} property={property} />

)

)}

</InfiniteScroll>

)

}

#### SWR Infinite Loading

typescript
import useSWRInfinite from &#039;swr/infinite&#039; class="kw">const useInfinitePropertiesSWR = (filters: PropertyFilters) => {

class="kw">const getKey = (pageIndex: number, previousPageData: any) => {

class="kw">if (previousPageData && !previousPageData.hasNext) class="kw">return null

class="kw">return /api/properties?page=${pageIndex}&${new URLSearchParams(filters)}

}

class="kw">return useSWRInfinite(getKey, fetcher)

}

Handling Complex State Synchronization

In PropTech applications, you often need to synchronize related data. Here's how each library handles dependent queries:

typescript
// TanStack Query dependent queries class="kw">const usePropertyWithDetails = (propertyId: string) => {

class="kw">const propertyQuery = useQuery({

queryKey: [&#039;property&#039;, propertyId],

queryFn: () => fetchProperty(propertyId),

})

class="kw">const neighborhoodQuery = useQuery({

queryKey: [&#039;neighborhood&#039;, propertyQuery.data?.neighborhoodId],

queryFn: () => fetchNeighborhood(propertyQuery.data.neighborhoodId),

enabled: !!propertyQuery.data?.neighborhoodId,

})

class="kw">const marketDataQuery = useQuery({

queryKey: [&#039;marketData&#039;, propertyQuery.data?.zipCode],

queryFn: () => fetchMarketData(propertyQuery.data.zipCode),

enabled: !!propertyQuery.data?.zipCode,

})

class="kw">return {

property: propertyQuery.data,

neighborhood: neighborhoodQuery.data,

marketData: marketDataQuery.data,

isLoading: propertyQuery.isLoading,

error: propertyQuery.error || neighborhoodQuery.error || marketDataQuery.error,

}

}

💡
Pro Tip
When working with dependent queries, always use the enabled option to prevent unnecessary API calls and improve performance.

Best Practices and Performance Optimization

Choosing the Right Library for Your Use Case

The decision between these libraries depends on your specific requirements:

#### Choose TanStack Query When:

  • Building complex applications with sophisticated data requirements
  • Need advanced features like infinite queries, parallel queries, and mutations
  • Want comprehensive DevTools for debugging
  • Planning to expand beyond React (Vue, Svelte support)
  • Building PropTech platforms with complex property data relationships

#### Choose SWR When:

  • Building with Next.js and want seamless integration
  • Prefer a lightweight solution with minimal bundle impact
  • Need simple, effective caching with good defaults
  • Want to implement custom fetching logic easily
  • Building content-heavy applications where stale-while-revalidate shines

Performance Optimization Strategies

#### Query Key Management

typescript
// Good: Consistent, predictable query keys class="kw">const propertyKeys = {

all: [&#039;properties&#039;] as class="kw">const,

lists: () => [...propertyKeys.all, &#039;list&#039;] as class="kw">const,

list: (filters: PropertyFilters) => [...propertyKeys.lists(), { filters }] as class="kw">const,

details: () => [...propertyKeys.all, &#039;detail&#039;] as class="kw">const,

detail: (id: string) => [...propertyKeys.details(), id] as class="kw">const,

}

// Usage class="kw">const { data } = useQuery({

queryKey: propertyKeys.detail(propertyId),

queryFn: () => fetchProperty(propertyId),

})

#### Smart Caching Strategies

typescript
// Configure different stale times based on data volatility class="kw">const usePropertyData = (propertyId: string) => {

// Property details change infrequently

class="kw">const propertyQuery = useQuery({

queryKey: [&#039;property&#039;, propertyId],

queryFn: () => fetchProperty(propertyId),

staleTime: 10 60 1000, // 10 minutes

})

// Market data changes frequently

class="kw">const marketQuery = useQuery({

queryKey: [&#039;market&#039;, propertyQuery.data?.zipCode],

queryFn: () => fetchMarketData(propertyQuery.data.zipCode),

staleTime: 30 * 1000, // 30 seconds

enabled: !!propertyQuery.data?.zipCode,

})

class="kw">return { property: propertyQuery.data, market: marketQuery.data }

}

Error Handling and User Experience

typescript
// Comprehensive error handling class="kw">const useResilientPropertyQuery = (propertyId: string) => {

class="kw">return useQuery({

queryKey: [&#039;property&#039;, propertyId],

queryFn: class="kw">async () => {

try {

class="kw">return class="kw">await fetchProperty(propertyId)

} catch (error) {

class="kw">if (error.status === 404) {

throw new Error(&#039;Property not found&#039;)

}

throw error

}

},

retry: (failureCount, error) => {

class="kw">if (error.message === &#039;Property not found&#039;) class="kw">return false

class="kw">return failureCount < 3

},

retryDelay: attemptIndex => Math.min(1000 2 * attemptIndex, 30000),

})

}

⚠️
Warning
Always implement proper error boundaries and fallback UI components to handle network failures gracefully, especially in PropTech applications where users expect real-time data.

Testing Strategies

typescript
// Mock queries class="kw">for testing import { QueryClient } from &#039;@tanstack/react-query&#039; import { renderWithQuery } from &#039;../test-utils&#039; describe(&#039;PropertyDetails&#039;, () => {

it(&#039;renders property data correctly&#039;, class="kw">async () => {

class="kw">const queryClient = new QueryClient({

defaultOptions: {

queries: { retry: false },

mutations: { retry: false },

},

})

// Prefill cache

queryClient.setQueryData([&#039;property&#039;, &#039;123&#039;], mockPropertyData)

class="kw">const { findByText } = renderWithQuery(

<PropertyDetails propertyId="123" />,

queryClient

)

expect(class="kw">await findByText(&#039;Modern Downtown Condo&#039;)).toBeInTheDocument()

})

})

The 2025 Verdict: Making Your Decision

As we look at the data fetching landscape in 2025, each library has carved out its niche. At PropTechUSA.ai, where we handle complex property data workflows, real-time market updates, and sophisticated user interactions, the choice often comes down to project scope and team expertise.

TanStack Query: The Comprehensive Solution

TanStack Query has emerged as the most feature-complete solution. Its v5 release brings improved TypeScript support, better performance, and framework-agnostic capabilities. The learning curve is steeper, but the payoff is substantial for complex applications.

Best for: Large PropTech platforms, applications with complex data relationships, teams that need advanced debugging and optimization tools.

SWR: The Elegant Minimalist

SWR continues to excel in its simplicity and effectiveness. Its small bundle size and excellent Next.js integration make it perfect for projects where simplicity and performance are paramount.

Best for: Content-heavy sites, Next.js applications, projects prioritizing bundle size, teams preferring minimal configuration.

Migration Considerations

If you're currently using React Query v3, migrating to TanStack Query v5 provides significant benefits but requires careful planning. SWR offers easier migration paths and can be introduced incrementally.

The future of React data fetching is bright, with all three libraries continuing active development. Your choice should align with your team's expertise, project requirements, and long-term maintenance goals. Whether you're building the next generation of property management tools or creating immersive real estate experiences, any of these libraries will serve you well when implemented thoughtfully.

Ready to implement robust data fetching in your PropTech application? Start with a proof of concept using your chosen library and gradually migrate your most critical data flows. The investment in proper data management will pay dividends in application performance, developer experience, and user satisfaction.

Need This Built?
We build production-grade systems with the exact tech covered in this article.
Start Your Project
PT
PropTechUSA.ai Engineering
Technical Content
Deep technical content from the team building production systems with Cloudflare Workers, AI APIs, and modern web infrastructure.