Setting up Axios for network requests in React Native applications
In React Native you can use the native fetch, but Axios provides interceptors, automatic JSON transformation, request cancellation via AbortController and convenient error handling. For a production application this is more important than saving one package.
Installation and basic client
npm install axios
Create a typed API client — don't use global axios directly:
import axios, { AxiosInstance, InternalAxiosRequestConfig } from 'axios';
const apiClient: AxiosInstance = axios.create({
baseURL: process.env.API_BASE_URL ?? 'https://api.example.com/v1',
timeout: 10_000,
headers: { 'Content-Type': 'application/json' },
});
Interceptors
Auth interceptor with token refresh:
apiClient.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const token = tokenStore.getAccessToken();
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
}
);
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
const newToken = await tokenStore.refresh();
originalRequest.headers.Authorization = `Bearer ${newToken}`;
return apiClient(originalRequest);
} catch {
tokenStore.clear();
navigationRef.navigate('Login');
}
}
return Promise.reject(error);
}
);
The _retry flag prevents infinite loop on refresh error. Without it 401 on /refresh → new refresh → 401 → infinity.
Logging in dev mode via axios-logger or custom interceptor:
if (__DEV__) {
apiClient.interceptors.request.use((config) => {
console.log(`→ ${config.method?.toUpperCase()} ${config.url}`);
return config;
});
}
Response typing
interface PaginatedResponse<T> {
data: T[];
meta: { total: number; page: number; perPage: number };
}
async function getProducts(page: number): Promise<PaginatedResponse<Product>> {
const { data } = await apiClient.get<PaginatedResponse<Product>>('/products', {
params: { page, per_page: 20 },
});
return data;
}
Request cancellation
React Native 0.71+ supports AbortController natively. Axios integrates with it:
const controller = new AbortController();
apiClient.get('/feed', { signal: controller.signal });
// In useEffect cleanup:
return () => controller.abort();
Without canceling requests in useEffect cleanup — possible setState on unmounted component warning and memory leak when quickly navigating between screens.
Error handling
function isAxiosError(error: unknown): error is AxiosError {
return axios.isAxiosError(error);
}
try {
await getProducts(1);
} catch (error) {
if (isAxiosError(error)) {
if (!error.response) {
// Network error — no connection
} else {
const status = error.response.status;
// 400, 422, 500...
}
}
}
Integration with React Query (@tanstack/react-query) or SWR moves error handling and caching to the library level — recommended for most applications.
Timelines
Basic setup with auth interceptor: 3–6 hours. With typing, React Query and error handling: 1–2 days. Pricing calculated individually.







