DEV Community

Cover image for How to Fetch Data in React: 5 Methods Compared
Carlos Chao(El Frontend)
Carlos Chao(El Frontend) Subscriber

Posted on

How to Fetch Data in React: 5 Methods Compared

Difficulty: Beginner

Reading time: ~8 minutes

Example API: https://rickandmortyapi.com

Fetching data is one of the first challenges every React developer faces.

In this article we’ll explore five different approaches—ranging from native browser features to full‑blown data‑fetching libraries—all with the same public API so you can compare them apples‑to‑apples.


Table of Contents

  1. Fetch API with useEffect
  2. Axios with useEffect
  3. A Reusable useFetch Hook
  4. React Query (TanStack Query)
  5. SWR (stale‑while‑revalidate)
  6. Choosing the Right Tool
  7. Wrapping Up

1. Fetch API with useEffect

The fastest way to pull data is the browser‑native fetch wrapped in a useEffect hook:

import React, { useState, useEffect } from "react";

function CharacterListFetch() {
  const [characters, setCharacters] = useState([]);

  useEffect(() => {
    fetch("https://rickandmortyapi.com/api/character")
      .then((res) => res.json())
      .then((data) => setCharacters(data.results))
      .catch((err) => console.error("Error:", err));
  }, []);

  return (
    <>
      <h2>Characters (Fetch API)</h2>
      <ul>
        {characters.map((c) => (
          <li key={c.id}>{c.name}</li>
        ))}
      </ul>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Pros & Cons

✅ Pros ⚠️ Cons
Zero dependencies Manual parsing (res.json())
Perfect for tiny demos Must handle caching, retries, etc. yourself

2. Axios with useEffect

Axios offers a nicer API and extra features (interceptors, cancellation…).

import React, { useState, useEffect } from "react";
import axios from "axios";

function CharacterListAxios() {
  const [characters, setCharacters] = useState([]);

  useEffect(() => {
    axios
      .get("https://rickandmortyapi.com/api/character")
      .then((res) => setCharacters(res.data.results)) // already JSON
      .catch((err) => console.error("Error:", err));
  }, []);

  return (
    <>
      <h2>Characters (Axios)</h2>
      <ul>
        {characters.map((c) => (
          <li key={c.id}>{c.name}</li>
        ))}
      </ul>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

3. A Reusable useFetch Hook

When you start duplicating fetch logic, extract it into a custom hook.

// useFetch.js
import { useState, useEffect } from "react";

export function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    setLoading(true);
    setError(null);

    fetch(url)
      .then((res) => res.json())
      .then((json) => {
        setData(json);
        setLoading(false);
      })
      .catch((err) => {
        setError(err);
        setLoading(false);
      });
  }, [url]);

  return { data, loading, error };
}
Enter fullscreen mode Exit fullscreen mode

Usage:

import { useFetch } from "./useFetch";

function CharacterListHook() {
  const { data, loading, error } = useFetch(
    "https://rickandmortyapi.com/api/character"
  );

  if (loading) return <p>Loading…</p>;
  if (error)   return <p>Error loading data</p>;

  return (
    <>
      <h2>Characters (Custom Hook)</h2>
      <ul>
        {data.results.map((c) => (
          <li key={c.id}>{c.name}</li>
        ))}
      </ul>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

4. React Query (TanStack Query)

For production apps with complex data flows, React Query is a powerhouse.

npm install @tanstack/react-query
Enter fullscreen mode Exit fullscreen mode
import { QueryClient, QueryClientProvider, useQuery } from "@tanstack/react-query";

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <CharacterListReactQuery />
    </QueryClientProvider>
  );
}

function CharacterListReactQuery() {
  const { data, error, isLoading } = useQuery({
    queryKey: ["characters"],
    queryFn: async () =>
      (await fetch("https://rickandmortyapi.com/api/character")).json(),
  });

  if (isLoading) return <p>Loading…</p>;
  if (error)     return <p>Error loading data</p>;

  return (
    <>
      <h2>Characters (React Query)</h2>
      <ul>
        {data.results.map((c) => (
          <li key={c.id}>{c.name}</li>
        ))}
      </ul>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Highlights

  • Built‑in caching & background refetch
  • Stale‑time tuning, pagination, mutations
  • Devtools for debugging

5. SWR (stale‑while‑revalidate)

Vercel’s SWR is minimal but mighty, ideal for Next.js projects.

npm install swr
Enter fullscreen mode Exit fullscreen mode
import useSWR from "swr";

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

function CharacterListSWR() {
  const { data, error, isLoading } = useSWR(
    "https://rickandmortyapi.com/api/character",
    fetcher
  );

  if (isLoading) return <p>Loading…</p>;
  if (error)     return <p>Error loading data</p>;

  return (
    <>
      <h2>Characters (SWR)</h2>
      <ul>
        {data.results.map((c) => (
          <li key={c.id}>{c.name}</li>
        ))}
      </ul>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

6. Choosing the Right Tool

Scenario Recommended Approach
Small tutorial, no extra deps Fetch API
Need features like interceptors Axios
Reusing simple fetch logic Custom hook
Large‑scale app, mutations, caching React Query
Lightweight cache, Next.js SWR

Tip: You can mix & match—e.g., use Fetch for one‑offs and React Query for critical data.


7. Wrapping Up

That’s it! You now have five practical patterns for fetching data in React:

  1. Fetch API
  2. Axios
  3. Custom hook
  4. React Query
  5. SWR

Pick the one that suits your project’s complexity and your team’s preferences.

Questions or feedback? Drop a comment below—let’s chat!

If this post helped you, please ❤️ like, 🦄 unicorn, or 📣 share it with your fellow devs.

Happy coding!

Top comments (0)