Choosing a React Architecture: SPA, Frameworks, and the Future of Data Fetching
Martin Rojas
♦♦5 min read
Learn how SPA and framework-based architectures in React impact data-fetching. Explore modern tools like React Router loaders, Tanstack Query, and React Server Components for building scalable apps.
Data-fetching strategies in React have become a hot topic, and for good reason. They are tightly coupled to your app’s architecture, meaning the decision to build a Single Page Application (SPA) or use a framework like NextJS or Remix often dictates your approach to fetching data. In fact, the React team's own documentation suggests frameworks for most projects—and compelling reasons.
This post explores why the choice between SPA and a framework matters so much, the trade-offs between client-side and server-side fetching, and how modern tools like React Router, loaders, and React Server Components (RSCs) are changing the landscape of React development.
Should You Use a Framework?
React frameworks like NextJS and Remix are often seen as hybrids, blending the best aspects of SPAs and server-rendered apps (MPAs). They aim to avoid the pitfalls of SPAs, such as network waterfalls and lack of preloading, while retaining their interactivity and seamless navigation.
The React team is clear in their recommendation:
Choosing a React Architecture: SPA, Frameworks, and the Future of Data Fetching · NextSteps Blog
"If you’re building a new app or a site fully with React, we recommend using a framework."
— React Docs
Without a framework, SPAs rely on client-side data-fetching strategies—often using hooks like useEffect—which the React team no longer recommends. Let’s unpack why that is.
What's Wrong with Client-Side Fetching?
SPAs popularized the approach of fetching data on the client, often triggered by React’s useEffect. However, there are several drawbacks to this method, as outlined by the React team:
1. Effects Don't Run on the Server
Fetching data with useEffect is inherently client-side. This prevents SPAs from leveraging server-side capabilities like pre-rendering or faster response times for initial data loads.
2. Network Waterfalls
Fetching data directly within components can lead to "waterfalls." For instance, a parent component fetches data, renders, and only then do child components fetch their data. This sequential fetching delays rendering and creates a stair-step pattern in your network tab:
GET /products/1 ▓▓▓▓▓▓▓
GET /brands ▓▓▓▓▓▓
GET /products/related ▓▓▓▓▓▓
Frameworks mitigate this by fetching all necessary data in parallel, even before rendering.
3. No Preloading or Caching
With useEffect, you can’t preload data before a component renders, nor can you efficiently cache responses without extra effort. By contrast, tools like Tanstack Query and React Router loaders provide robust caching and preloading capabilities.
4. Ergonomic Challenges
Using useEffect for data-fetching requires managing race conditions, cleanup logic, and edge cases manually. Frameworks and third-party tools simplify these tasks with dedicated APIs.
Alternatives to useEffect
If useEffect isn’t ideal, what should you use instead? The React team offers two main recommendations:
Use a framework like NextJS or Remix.
For SPAs, use tools like React Router loaders or Tanstack Query.
Using React Router Loaders
React Router’s loader functions fetch data outside of React components. This avoids waterfalls by parallelizing data-fetching for all matched routes.
Example: Fetching Data with Loaders
Here’s how you can set up a loader in React Router:
This approach gives you the best of both worlds: robust caching and no waterfalls.
Fetching on the Server: Frameworks and React Server Components
Server-side fetching has gained traction thanks to frameworks like NextJS and Remix, which fetch data before sending HTML to the client. This reduces load times and eliminates the need for client-initiated fetching on the initial page load.
Framework-Specific Fetching
NextJS: Fetch data with getServerSideProps or getStaticProps.
Remix: Use loader functions, similar to React Router.
These approaches ensure data is ready when the page loads, avoiding the drawbacks of client-side fetching.
React Server Components (RSCs)
RSCs are a newer React feature designed to minimize client-side JavaScript by rendering components entirely on the server. Unlike traditional server-rendered components, RSCs don’t rehydrate on the client, meaning they can’t handle events or state.
RSCs are well-suited for static content or data-heavy components, while client components handle interactivity.
Which Architecture Should You Choose?
SPA: Suitable for simple, interactive apps. Use tools like React Router loaders or Tanstack Query to improve data-fetching ergonomics.
Framework: Best for modern apps with complex data-fetching needs. Frameworks provide out-of-the-box solutions for server-side fetching and rendering.
Hybrid: Frameworks like NextJS and Remix let you mix server and client components, giving you flexibility.
Conclusion
Your choice of React architecture—SPA or framework—dictates your data-fetching strategy. While frameworks are increasingly recommended for new projects, SPAs remain viable with tools like loaders and caching libraries.
Understanding the trade-offs between client-side and server-side fetching will help you make informed decisions as you build scalable, fast, and maintainable React apps.
Ready to dive deeper? Share your experiences or questions in the comments!