How to Fetch Api in React

Introduction React is the most widely used JavaScript library for building user interfaces, and one of its most common tasks is fetching data from external APIs. Whether you're pulling user profiles, product listings, or real-time analytics, the way you handle API requests directly impacts performance, reliability, and user experience. But not all methods are created equal. With dozens of librarie

Oct 25, 2025 - 13:23
Oct 25, 2025 - 13:23
 0

Introduction

React is the most widely used JavaScript library for building user interfaces, and one of its most common tasks is fetching data from external APIs. Whether you're pulling user profiles, product listings, or real-time analytics, the way you handle API requests directly impacts performance, reliability, and user experience. But not all methods are created equal. With dozens of libraries and approaches available, choosing the right one can be overwhelming.

This guide cuts through the noise. Weve evaluated the top 10 most trusted, widely adopted, and production-tested methods to fetch APIs in Reacteach selected based on community adoption, documentation quality, performance benchmarks, error handling capabilities, and maintainability. These are not theoretical suggestions. These are the tools used by Fortune 500 companies, high-traffic startups, and open-source maintainers who demand reliability under pressure.

By the end of this article, youll understand not just how to fetch data in React, but how to do it securely, efficiently, and at scale. Youll also learn when to use each method, what trade-offs to expect, and how to avoid common pitfalls that lead to memory leaks, stale data, or failed requests.

Why Trust Matters

In web development, trust isnt a luxuryits a requirement. When your React application fails to load data, users dont see a technical error. They see a broken product. A delayed API call can mean lost conversions. A memory leak from improper cleanup can crash mobile devices. A poorly handled error can expose sensitive information or leave users confused.

Many tutorials teach the bare-minimum Fetch API or simple useEffect hooks. While these work for learning, they fall apart under real-world conditions: network timeouts, concurrent requests, authentication headers, caching, retry logic, and server-side rendering. Trustworthy API fetching solutions anticipate these problems and provide built-in safeguards.

Trusted methods come with:

  • Automatic retry mechanisms for failed requests
  • Smart caching to reduce server load and improve perceived performance
  • Abort controllers to prevent memory leaks on component unmount
  • Type safety and TypeScript support
  • Community-backed documentation and active maintenance
  • Integration with state management and error boundaries

Using untrusted or outdated methods may save you time initially, but they cost you in debugging, performance degradation, and technical debt. The 10 methods listed below have been battle-tested across thousands of applications. Theyre not just populartheyre proven.

Top 10 How to Fetch API in React

1. Axios

Axios is one of the most mature and widely adopted HTTP clients for JavaScript. Its not React-specific, but its seamless integration with React makes it a top choice for enterprise applications.

Axios supports interceptors, automatic JSON parsing, request/response cancellation, and request timeouts out of the box. It works identically in browsers and Node.js, making it ideal for SSR (Server-Side Rendering) applications using Next.js.

Example:

import axios from 'axios';

import { useEffect, useState } from 'react';

function UserProfile() {

const [user, setUser] = useState(null);

const [loading, setLoading] = useState(true);

const [error, setError] = useState(null);

useEffect(() => {

const fetchUser = async () => {

try {

const response = await axios.get('/api/user', {

headers: { 'Authorization': Bearer ${token} }

});

setUser(response.data);

} catch (err) {

setError(err.response?.data?.message || 'Failed to fetch user');

} finally {

setLoading(false);

}

};

fetchUser();

}, []);

if (loading) return <p>Loading...</p>;

if (error) return <p>Error: {error}</p>;

return <div>{user?.name}</div>;

}

Axios excels in environments where you need fine-grained control over headers, interceptors for auth tokens, and unified error handling. Its promise-based API is familiar to developers coming from jQuery or vanilla JavaScript. While it adds ~2KB to your bundle, the trade-off is worth it for robustness.

2. React Query (TanStack Query)

React Query (now officially TanStack Query) is the gold standard for data fetching in React applications as of 2024. Its not just a fetcherits a full-featured data synchronization library that handles caching, background updates, pagination, mutations, and more.

With React Query, you declare what data you need, and the library takes care of everything else: automatic refetching on focus, stale-while-revalidate caching, deduplication of identical requests, and retry logic with exponential backoff.

Example:

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

import { axios } from './apiClient';

function UserProfile() {

const { data: user, isLoading, error } = useQuery({

queryKey: ['user'],

queryFn: () => axios.get('/api/user').then(res => res.data),

staleTime: 5 * 60 * 1000, // 5 minutes

retry: 3,

refetchOnWindowFocus: true,

});

if (isLoading) return <p>Loading...</p>;

if (error) return <p>Error: {error.message}</p>;

return <div>{user?.name}</div>;

}

React Query eliminates the need for manual state management of loading, error, and data states. It also supports infinite scrolling, optimistic updates, and server-side synchronization out of the box. Over 1.2 million downloads per week make it the most trusted solution for complex React applications.

3. SWR (Stale-While-Revalidate)

SWR, developed by Vercel (the team behind Next.js), is a lightweight, React-first data fetching hook inspired by React Query. It follows the stale-while-revalidate caching strategy, meaning it returns cached data immediately while fetching fresh data in the background.

SWR is minimalistic, easy to learn, and integrates beautifully with Next.js. Its perfect for applications that prioritize speed and simplicity without sacrificing reliability.

Example:

import useSWR from 'swr';

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

function UserProfile() {

const { data, error, isLoading } = useSWR('/api/user', fetcher, {

revalidateOnFocus: true,

dedupingInterval: 2000,

});

if (isLoading) return <p>Loading...</p>;

if (error) return <p>Error: {error.message}</p>;

return <div>{data?.name}</div>;

}

SWRs key advantages are its tiny bundle size (~1KB gzipped), automatic revalidation, and built-in support for optimistic updates. Its ideal for content-heavy sites like blogs, dashboards, and e-commerce product pages where speed and responsiveness are critical.

4. Native Fetch API with Custom Hook

While the native Fetch API is not a library, its the foundation of most modern data fetching solutions. Many teams choose to wrap it in a custom hook to standardize behavior across the application.

Using Fetch directly gives you full control and zero external dependencies. Its ideal for lightweight apps or when you want to avoid bundle bloat.

Example of a reusable custom hook:

import { useState, useEffect } from 'react';

function useApi(url, options = {}) {

const [data, setData] = useState(null);

const [loading, setLoading] = useState(true);

const [error, setError] = useState(null);

useEffect(() => {

const controller = new AbortController();

const fetchData = async () => {

try {

const response = await fetch(url, {

...options,

signal: controller.signal

});

if (!response.ok) {

throw new Error(HTTP error! status: ${response.status});

}

const result = await response.json();

setData(result);

} catch (err) {

if (err.name === 'AbortError') return;

setError(err.message);

} finally {

setLoading(false);

}

};

fetchData();

return () => controller.abort();

}, [url, options]);

return { data, loading, error };

}

// Usage

function UserProfile() {

const { data, loading, error } = useApi('/api/user', {

headers: { 'Authorization': Bearer ${token} }

});

if (loading) return <p>Loading...</p>;

if (error) return <p>Error: {error}</p>;

return <div>{data?.name}</div>;

}

This approach is trustworthy because its transparent, testable, and portable. You own the logic. Its perfect for teams that want to avoid third-party dependencies while maintaining consistency and reliability.

5. React Query with Next.js App Router

With the introduction of the Next.js App Router, React Query has evolved to support server components and streaming. This integration allows you to prefetch data on the server and hydrate it on the client without additional client-side fetching.

Using React Query with the App Router, you can eliminate client-side hydration flicker and reduce time-to-interactive by preloading data during server-side rendering.

Example in a Server Component:

// app/user/page.js

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

import UserProfileClient from './UserProfileClient';

const queryClient = new QueryClient();

export default async function UserPage() {

// Pre-fetch data on server

await queryClient.prefetchQuery({

queryKey: ['user'],

queryFn: () => fetch('/api/user').then(res => res.json())

});

return (

<QueryClientProvider client={queryClient}>

<UserProfileClient />

</QueryClientProvider>

);

}

And the client component:

'use client';

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

function UserProfileClient() {

const { data, isLoading, error } = useQuery({

queryKey: ['user'],

queryFn: () => fetch('/api/user').then(res => res.json()),

});

if (isLoading) return <p>Loading...</p>;

if (error) return <p>Error: {error.message}</p>;

return <div>{data?.name}</div>;

}

This pattern ensures the user sees content instantly on load, with no loading spinner. Its the most performant way to fetch data in modern React applications and is trusted by companies like Airbnb, Shopify, and Notion.

6. GraphQL with Apollo Client

When your application requires complex data relationshipssuch as nested user profiles with comments, likes, and postsGraphQL is the superior choice. Apollo Client is the most trusted GraphQL library for React.

Apollo provides automatic caching, normalized data storage, mutations, subscriptions, and type-safe queries via TypeScript. It also supports offline caching and persistent queries.

Example:

import { useQuery } from '@apollo/client';

import { gql } from '@apollo/client';

const GET_USER = gql

query GetUser($id: ID!) {

user(id: $id) {

id

name

email

posts {

title

content

}

}

}

;

function UserProfile({ userId }) {

const { loading, error, data } = useQuery(GET_USER, {

variables: { id: userId },

fetchPolicy: 'cache-and-network',

});

if (loading) return <p>Loading...</p>;

if (error) return <p>Error: {error.message}</p>;

return (

<div>

<h2>{data.user.name}</h2>

<ul>

{data.user.posts.map(post => (

<li key={post.title}>{post.title}</li>

))}

</ul>

</div>

);

}

Apollo Client is trusted by Netflix, GitHub, and IBM for its ability to reduce over-fetching, simplify data normalization, and provide predictable updates. Its the go-to solution for applications with complex, interconnected data models.

7. REST with React Suspense and Server Components (Experimental but Promising)

React 18 introduced Suspense for data fetching, and with the App Router, you can now suspend rendering while data loadswithout writing any useEffect or loading states manually.

This approach uses a Promise-based data fetcher that throws during rendering. React suspends the component tree until the data resolves.

Example:

// lib/fetchUser.js

export function fetchUser() {

const promise = fetch('/api/user').then(res => res.json());

return promise;

}

// app/user/page.js

import { fetchUser } from '@/lib/fetchUser';

export default async function UserPage() {

const user = await fetchUser();

return (

<div>

<h1>{user.name}</h1>

<p>{user.email}</p>

</div>

);

}

While this is still evolving, its already being adopted by teams using Next.js 13+ for its simplicity and performance. No loading states. No error boundaries. Just declarative data fetching. Its not suitable for all use cases (e.g., client-side interactions), but for static or semi-static pages, its the most elegant solution available.

8. RTK Query (Redux Toolkit Query)

RTK Query is a data fetching and caching layer built into Redux Toolkit. Its designed to replace thunks and sagas for API calls, offering automatic code generation, caching, and mutation handling.

Its ideal for applications already using Redux and needing a scalable, type-safe way to manage API state without adding extra libraries.

Example:

// api/apiSlice.js

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const apiSlice = createApi({

baseQuery: fetchBaseQuery({ baseUrl: '/api' }),

endpoints: (builder) => ({

getUser: builder.query({

query: (id) => /user/${id},

}),

}),

});

export const { useGetUserQuery } = apiSlice;

// Component

import { useGetUserQuery } from '@/api/apiSlice';

function UserProfile({ userId }) {

const { data, isLoading, error } = useGetUserQuery(userId);

if (isLoading) return <p>Loading...</p>;

if (error) return <p>Error: {error.message}</p>;

return <div>{data?.name}</div>;

}

RTK Query integrates seamlessly with Redux DevTools, supports code splitting, and provides automatic revalidation and cache invalidation. Its trusted by large-scale enterprise apps that rely on Redux for state management and need a unified approach to data.

9. React-Query with Suspense (Hybrid Approach)

React Query supports Suspense out of the box, allowing you to combine the power of React Querys caching with Reacts declarative loading system.

This approach lets you use Suspense for UI-level loading states while letting React Query handle the caching, retries, and background updates.

Example:

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

function UserProfile({ userId }) {

const query = useQuery({

queryKey: ['user', userId],

queryFn: () => fetch(/api/user/${userId}).then(res => res.json()),

suspense: true, // Enables Suspense

});

return <div>{query.data?.name}</div>;

}

// In parent component

import { Suspense } from 'react';

function App() {

return (

<Suspense fallback="Loading user...">

<UserProfile userId="123" />

</Suspense>

);

}

This pattern is powerful because it separates concerns: React handles UI state (loading/error), and React Query handles data state (caching, refetching, deduplication). Its a favorite among teams building complex UIs with nested components that need independent loading states.

10. Custom Fetch Wrapper with Zod Validation

For teams prioritizing type safety and data integrity, combining a custom fetch wrapper with Zod (a TypeScript-first schema validation library) creates a bulletproof data pipeline.

Zod validates API responses at runtime, ensuring your components receive only correctly shaped dataeven if the backend sends malformed JSON.

Example:

import { z } from 'zod';

import { useState, useEffect } from 'react';

const UserSchema = z.object({

id: z.number(),

name: z.string(),

email: z.string().email(),

});

type User = z.infer;

async function fetchUser(id) {

const res = await fetch(/api/user/${id});

if (!res.ok) throw new Error('Network error');

const json = await res.json();

return UserSchema.parse(json); // Throws if invalid

}

function UserProfile({ id }) {

const [user, setUser] = useState(null);

const [error, setError] = useState(null);

const [loading, setLoading] = useState(true);

useEffect(() => {

const load = async () => {

try {

const data = await fetchUser(id);

setUser(data);

} catch (err) {

setError(err instanceof Error ? err.message : 'Unknown error');

} finally {

setLoading(false);

}

};

load();

}, [id]);

if (loading) return <p>Loading...</p>;

if (error) return <p>Error: {error}</p>;

return <div>{user?.name}</div>;

}

This method is trusted by fintech, healthcare, and government applications where data integrity is non-negotiable. Zod catches bugs early, prevents UI crashes from malformed responses, and provides excellent TypeScript integration.

Comparison Table

Method Bundle Size Caching Retry Logic TypeScript Support SSR Ready Best For
Axios ~2KB No (manual) Yes Yes Yes Enterprise apps needing interceptors and control
React Query ~5KB Yes (advanced) Yes (configurable) Yes Yes Complex apps with caching, pagination, mutations
SWR ~1KB Yes (stale-while-revalidate) Yes Yes Yes Speed-focused apps, Next.js projects
Native Fetch + Hook 0KB No (manual) No (manual) Yes Yes Minimalist apps, learning, avoiding dependencies
React Query + Next.js App Router ~5KB Yes Yes Yes Yes (server prefetch) High-performance Next.js apps
Apollo Client ~15KB Yes (normalized) Yes Yes Yes GraphQL APIs, complex data relationships
React Suspense (Server Components) 0KB (built-in) No No Yes Yes Static or semi-static pages
RTK Query ~8KB Yes Yes Yes Yes Redux-based apps needing unified data layer
React Query + Suspense ~5KB Yes Yes Yes Yes UI-heavy apps with nested components
Fetch + Zod ~1KB (Zod: ~5KB) No (manual) No (manual) Yes (strongest) Yes Security-critical apps requiring data validation

FAQs

What is the fastest way to fetch an API in React?

SWR is typically the fastest for client-side rendering due to its minimal bundle size and built-in stale-while-revalidate caching. For server-side rendering, React Suspense with Next.js App Router delivers the fastest perceived load time because content is rendered on the server before the client even loads.

Should I use Axios or Fetch in React?

Use Axios if you need interceptors, automatic JSON parsing, and built-in timeout/retry logic. Use the native Fetch API if you want zero dependencies and full control over the request lifecycle. Both are trustworthychoose based on your projects complexity and team preferences.

Is React Query better than Axios?

They serve different purposes. Axios is an HTTP client. React Query is a data synchronization library. Many teams use both: Axios for making requests, and React Query for caching, retrying, and managing state. React Query is superior for complex applications, but Axios is simpler for basic needs.

Can I use React Query with Next.js?

Yes, and its highly recommended. React Query integrates seamlessly with Next.js App Router. You can prefetch data on the server, hydrate it on the client, and maintain consistent caching across SSR and CSR.

Do I need Redux if I use RTK Query?

No. RTK Query is built into Redux Toolkit but doesnt require you to use Redux state manually. You can use RTK Query alone for data fetching and skip writing reducers or actions. However, if you already use Redux for global state, RTK Query complements it perfectly.

Is it safe to use the native Fetch API in production?

Yes, if wrapped in a well-designed custom hook with error handling, abort controllers, and retry logic. Many large-scale applications use it successfully. The key is not the API itself, but how you manage its lifecycle and edge cases.

When should I use GraphQL instead of REST?

Use GraphQL when you need to fetch nested, related data in a single request (e.g., user + posts + comments). Use REST when your data is simple, hierarchical, or youre working with third-party APIs that dont support GraphQL. GraphQL reduces over-fetching but adds complexity.

Does SWR work with authentication?

Yes. SWR supports custom fetchers. You can pass headers or tokens via the fetcher function. For example: useSWR('/api/user', (url) => fetch(url, { headers: { Authorization: token } }).

Whats the best method for a small personal project?

For a small project with minimal API calls, use SWR or a custom Fetch hook. Both are lightweight, easy to understand, and require no complex setup. Avoid Apollo or RTK Query unless youre building something scalable.

How do I handle API errors gracefully in React?

Always use try/catch blocks with async/await, or .catch() with promises. Use React Query or SWR for automatic retry and error boundaries. For custom hooks, return an error state and display user-friendly messages. Never show raw error strings to users.

Conclusion

Finding the right way to fetch APIs in React isnt about choosing the most popular libraryits about choosing the right tool for your applications needs. The 10 methods outlined here represent the pinnacle of reliability, performance, and maintainability in 2024.

Axios gives you control. React Query gives you power. SWR gives you speed. Native Fetch gives you simplicity. GraphQL with Apollo gives you precision. Zod gives you safety. Suspense gives you elegance. RTK Query gives you integration. Each has its place.

Trust isnt earned by hypeits earned by consistency, community adoption, and resilience under pressure. These tools have been tested across thousands of applications, from startups to Fortune 500 companies. They dont just workthey scale.

As you build your next React application, dont reach for the first tutorial you find. Evaluate your data needs: Is it simple or complex? Static or dynamic? Client-side or server-rendered? Do you need caching, validation, or real-time updates?

Then choose wisely. The right API fetching method doesnt just get your datait transforms your application from functional to exceptional.