When building a React app with Vite that talks to a REST API, you need a way to fetch data. You can install a powerful data-fetching library like React Query, or you can just use the browser's native fetch function inside a useEffect hook.
Choosing the wrong approach early on leads to either messy boilerplate code or unnecessary bundle bloat. Let us look at how both options actually work in practice.
- The pros and cons of using native fetch with React hooks
- How React Query automates caching and background updates
- Real code examples for both fetching patterns
- A clear recommendation based on your project size
The native fetch approach
Native fetch is built right into your browser, meaning zero extra downloads for your users. You write standard JavaScript to grab data and store it in React's local state.
Here is how you fetch a user profile using native fetch inside a component:
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(`https://api.example.com/users/${userId}`)
.then(res => res.json())
.then(data => {
setUser(data);
setLoading(false);
});
}, [userId]);
if (loading) return <p>Loading...</p>;
return <div>{user.name}</div>;
}The key takeaway here is total control with zero dependencies, but you have to write your own loading, error, and caching logic from scratch.
The React Query approach
React Query (officially called TanStack Query) is a dedicated library for managing server state. Server state is data that lives on a remote backend, which your app needs to download, cache, and update.
Here is the exact same user profile fetched using React Query:
import { useQuery } from '@tanstack/react-query';
function UserProfile({ userId }) {
const { data: user, isLoading } = useQuery({
queryKey: ['user', userId],
queryFn: () =>
fetch(`https://api.example.com/users/${userId}`).then(res => res.json())
});
if (isLoading) return <p>Loading...</p>;
return <div>{user.name}</div>;
}The key takeaway is that React Query writes most of the boring data-fetching plumbing for you, including automatic background refetching and caching.
Common mistakes
One common mistake with native fetch is forgetting to handle race conditions when a user rapidly changes pages. This happens when an older API response arrives after a newer one, accidentally overwriting fresh data with stale data.
Another common mistake with React Query is adding it to a tiny portfolio site or a simple CRUD app that only makes two API calls. In small apps, React Query adds extra bundle weight and complexity that you simply do not need.
Conclusion
Use native fetch if you are building a small portfolio project, a prototype, or want to master the fundamentals of React hooks. Install React Query if your app has multiple pages, complex user interactions, and needs automatic background data synchronization.
Open your current Vite project right now and check how many components manually handle loading states, then decide if you need a library to automate it.