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 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.
| Feature | fetch() | Axios |
|---|---|---|
| Installation | Built into browsers | npm install axios |
| JSON parsing | Manual res.json() | Automatic, via res.data |
| Error on 404/500 | Does NOT reject the promise | Rejects automatically |
| Request/response interceptors | Not built in | Built in |
| Base URL config | Manual string concatenation | baseURL option |
| Request timeout | Needs AbortController | timeout 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.
npm install axiosOnce installed, import it at the top of any file where you need to make a request.
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.
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;Loading users...
(after the request resolves)
• Leanne Graham
• Ervin Howell
• Clementine BauchMaking 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.
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>
);
}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.
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.
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.
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
- 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.
- 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.