LearnContact
Lesson 5122 min read

Axios

Learn how to install and use Axios, a popular HTTP client, to make cleaner GET and POST requests in React than the built-in fetch() API.

Introduction

In the previous lesson you used the browser's built-in fetch() API to talk to a server. fetch() works fine, but it is quite low-level: you have to manually check response.ok, manually call .json(), and manually build headers and request bodies every single time. As an application grows and makes dozens of API calls, this repetition adds up.

Axios is a small, promise-based HTTP client library that wraps a lot of this boilerplate for you. It is one of the most downloaded packages in the entire JavaScript ecosystem, and countless React codebases use it instead of fetch(). This lesson introduces Axios, compares it directly to fetch(), and shows how to use it for real requests inside a React component.

What You Will Learn
  • What Axios is and why developers reach for it over fetch().
  • How to install Axios in a React project.
  • How to make GET and POST requests with Axios inside useEffect.
  • How to configure a base URL and default headers.
  • How Axios error handling differs from fetch().

What is Axios?

Axios is a third-party JavaScript library for making HTTP requests, usable in both the browser and Node.js. It is not part of React — it is a general-purpose package that happens to be extremely popular in React projects because it removes a lot of the repetitive setup that fetch() requires.

Under the hood, Axios still uses the same underlying browser networking, but it gives you a friendlier API on top: automatic JSON parsing, built-in timeout support, and a system of "interceptors" that let you run code before every request is sent or after every response arrives.

Automatic JSON

Response bodies are parsed into JavaScript objects automatically — no manual .json() call needed.

Interceptors

Attach logic that runs on every outgoing request or incoming response, like adding an auth token.

Cleaner Errors

Non-2xx responses automatically reject the promise, so you can handle them in one catch block.

Wide Compatibility

Works consistently across browsers and Node.js, which historically mattered more than it does today.

Axios vs Fetch

fetch() is built into every modern browser and requires no installation, but it leaves several conveniences up to you. Axios trades a small dependency for a noticeably smoother developer experience. Here is the same GET request written both ways.

fetch()

  • fetch('/api/users')
  • .then((res) => {
  • if (!res.ok) {
  • throw new Error('Request failed');
  • }
  • return res.json();
  • })
  • .then((data) => setUsers(data))
  • .catch((err) => setError(err.message));

Axios

  • axios.get('/api/users')
  • .then((res) => setUsers(res.data))
  • .catch((err) => setError(err.message));
  • // res.data is already parsed JSON
  • // non-2xx responses reject automatically

Notice that Axios never requires an explicit res.ok check or a separate .json() call. The parsed body is always available at res.data, and any status code outside the 200-299 range automatically triggers the .catch() block.

Featurefetch()Axios
InstallationBuilt into browsersnpm install axios
JSON parsingManual res.json()Automatic, via res.data
Error on 404/500Does NOT reject the promiseRejects automatically
Request/response interceptorsNot built inBuilt in
Base URL configManual string concatenationbaseURL option
Request timeoutNeeds AbortControllertimeout option

Installing Axios

Because Axios is not built into the browser, it needs to be added to your project as a dependency before you can import it.

Terminal
npm install axios

Once installed, import it at the top of any file where you need to make a request.

Importing Axios
import axios from 'axios';

Making a GET Request

Just like fetch(), Axios requests are typically made inside a useEffect Hook so they run once when the component mounts. The response object's .data property already contains the parsed JSON.

UserList.jsx
import { useState, useEffect } from 'react';
import axios from 'axios';

function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    axios.get('https://jsonplaceholder.typicode.com/users')
      .then((res) => {
        setUsers(res.data);
        setLoading(false);
      })
      .catch((err) => {
        setError(err.message);
        setLoading(false);
      });
  }, []);

  if (loading) return <p>Loading users...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

export default UserList;
Rendered Output
Loading users...
(after the request resolves)
• Leanne Graham
• Ervin Howell
• Clementine Bauch

Making a POST Request

To send data to a server, pass the request body as the second argument to axios.post(). Unlike fetch(), you do not need to manually stringify the object or set a Content-Type header — Axios does both automatically for plain JavaScript objects.

CreatePost.jsx
function CreatePost() {
  const [title, setTitle] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();

    axios.post('https://jsonplaceholder.typicode.com/posts', {
      title,
      body: 'Post content here',
      userId: 1,
    })
      .then((res) => {
        console.log('Created post:', res.data);
      })
      .catch((err) => {
        console.error('Failed to create post:', err.message);
      });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input value={title} onChange={(e) => setTitle(e.target.value)} />
      <button type="submit">Create Post</button>
    </form>
  );
}
Console Output (after submit)
Created post: { id: 101, title: "My Title", body: "Post content here", userId: 1 }

Base URL and Default Headers

Repeating the full API URL and the same headers in every single call is tedious and error-prone. Axios lets you create a configured instance once, with axios.create(), and reuse it everywhere.

api/client.js
import axios from 'axios';

const api = axios.create({
  baseURL: 'https://jsonplaceholder.typicode.com',
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'Bearer YOUR_TOKEN',
  },
  timeout: 5000,
});

export default api;

Once created, every component can import this shared instance and call relative paths instead of full URLs.

UserList.jsx (using the shared instance)
import api from './api/client';

// Instead of the full URL, just the path:
api.get('/users').then((res) => setUsers(res.data));

Error Handling with Axios

One of the biggest practical differences from fetch() is how errors are represented. With fetch(), a 404 or 500 response still resolves the promise successfully — you have to check res.ok yourself. Axios treats any non-2xx status as a rejected promise, so a single .catch() (or a try/catch with async/await) reliably captures it.

Handling Axios Errors
async function loadUsers() {
  try {
    const res = await api.get('/users');
    setUsers(res.data);
  } catch (err) {
    if (err.response) {
      // Server responded with a status code outside 2xx
      console.error('Server error:', err.response.status);
    } else if (err.request) {
      // Request was made but no response received
      console.error('No response from server');
    } else {
      console.error('Request setup error:', err.message);
    }
  }
}

Common Mistakes

Avoid These Mistakes
  • Calling res.json() on an Axios response — Axios already gives you parsed data on res.data.
  • Forgetting that fetch() does NOT reject on 404/500, so old error-handling habits do not carry over automatically in reverse.
  • Creating a new axios.create() instance in every component instead of sharing one configured instance.
  • Hardcoding the full API URL in every request instead of using baseURL.
  • Not wrapping requests in try/catch or .catch(), leaving unhandled promise rejections.

Best Practices

  • Create one shared Axios instance with axios.create() and reuse it across your app.
  • Keep the base URL and default headers in a single, central file.
  • Prefer async/await with try/catch for readability over long .then() chains.
  • Always show loading and error states to the user, not just the happy path.
  • Use interceptors for cross-cutting concerns like attaching an auth token or logging every request.

Frequently Asked Questions

Do I have to use Axios instead of fetch()?

No. Both work fine in React. Axios is simply more convenient for larger apps because of automatic JSON parsing, cleaner error handling, and interceptors.

Is Axios built into React?

No, it is a separate npm package. You must run npm install axios before you can import it.

Why did my 404 response not trigger the .catch() block with fetch() but it did with Axios?

fetch() only rejects on network failures, not on HTTP error status codes. Axios rejects automatically for any response outside the 200-299 range.

What are interceptors used for?

Interceptors let you run code before a request is sent or after a response is received — commonly used to attach an auth token to every request or to log/redirect on certain error responses.

Can I use Axios outside of useEffect?

Yes. Axios calls are commonly made in useEffect for data fetching on mount, but also inside event handlers like form submissions or button clicks.

Key Takeaways

  • Axios is a popular third-party HTTP client that simplifies many things fetch() leaves manual.
  • Response data is available directly at res.data, already parsed as JSON.
  • Axios automatically rejects on non-2xx status codes, unlike fetch().
  • axios.create() lets you configure a shared base URL, headers, and timeout once.
  • Both GET and POST requests follow a clean, promise-based (or async/await) syntax.

Summary

Axios trades a small install step for a noticeably smoother data-fetching experience: automatic JSON parsing, sensible error rejection, and a reusable configured instance for base URLs and headers. Many real-world React codebases prefer it over the raw fetch() API for exactly these reasons.

In this lesson, you installed Axios, made GET and POST requests inside useEffect, configured a shared instance with a base URL and default headers, and compared its behavior directly against fetch(). Next, you will use these tools to work with a full REST API, covering all four CRUD operations.

Lesson 51 Completed
  • You understand what Axios is and how it differs from fetch().
  • You can make GET and POST requests with Axios.
  • You know how to configure a base URL and default headers.
  • You are ready to work with full REST APIs.
Next Lesson →

Working with REST APIs