React Native Login Api Example Axios

8 min read

React Native Login API Example with Axios: A Step‑by‑Step Guide

Building a secure login flow is one of the first challenges developers face when creating a mobile app with React Native. Even so, by combining React Native’s component model with Axios—a promise‑based HTTP client—you can easily communicate with a backend REST API, handle authentication tokens, and manage user state. Even so, this article walks you through a complete, production‑ready example that shows how to set up a React Native project, install Axios, create a reusable API service, design a login screen, process the response, store the token securely, and work through to the protected part of your app. Follow each section carefully, and you’ll have a working login implementation that you can adapt to any API specification.


1. Setting Up the React Native Project

Before writing any code, ensure you have the React Native CLI (or Expo) installed and a working development environment for Android/iOS.

# Using React Native CLI
npx react-native init LoginAppDemo
cd LoginAppDemo

# If you prefer Expo
# expo init LoginAppDemo
# cd LoginAppDemo

Once the project is created, run it on an emulator or device to verify the baseline works:

npx react-native run-android   # or run-ios

You should see the default welcome screen. From here we’ll add the dependencies needed for API communication and state management.


2. Installing Required Dependencies

The core library for making HTTP requests is Axios. We’ll also add @react-native-async-storage/async-storage to persist the JWT (or any token) returned by the login endpoint, and @react-navigation/native with its stack navigator to handle screen transitions after a successful login No workaround needed..

npm install axios @react-native-async-storage/async-storage @react-navigation/native @react-navigation/stack
# For Expo users, replace npm with expo install where needed

If you are using the bare React Native CLI, you may need to link native modules (though recent versions autolink). For AsyncStorage on iOS, run:

npx pod-install

3. Creating a Reusable Axios Service

Instead of scattering axios.Consider this: get or axios. Practically speaking, post calls throughout your components, centralize the configuration in a service file. This makes it easy to change the base URL, add headers, or implement interceptors for token refresh logic later.

Create a folder src/services/ and add api.js:

// src/services/api.js
import axios from 'axios';

// Replace with your actual backend URL
const BASE_URL = 'https://api.example.com';

const api = axios.create({
  baseURL: BASE_URL,
  timeout: 10000, // 10 seconds
  headers: {
    'Content-Type': 'application/json',
    // Accept header can be adjusted based on your API
    Accept: 'application/json',
  },
});

// Optional: request interceptor to attach auth token
api.In practice, getItem('userToken');
    if (token) {
      config. request.Even so, interceptors. headers.use(
  async (config) => {
    const token = await require('@react-native-async-storage/async-storage')
      .Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.

export default api;

Key points:

  • The baseURL ensures all endpoints are relative to your backend.
  • A timeout prevents hanging requests.
  • The request interceptor automatically injects a stored JWT into the Authorization header for every outgoing call—handy for protected routes later.

4. Designing the Login Screen

We’ll build a simple functional component using React hooks. The screen contains two TextInput fields (email and password), a button to submit, and basic loading/error states Worth keeping that in mind..

Create src/screens/LoginScreen.js:

// src/screens/LoginScreen.js
import React, { useState } from 'react';
import {
  View,
  TextInput,
  Button,
  Text,
  ActivityIndicator,
  StyleSheet,
  Alert,
} from 'react-native';
import api from '../services/api';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useNavigation } from '@react-navigation/native';

export default function LoginScreen() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [loading, setLoading] = useState(false);
  const navigation = useNavigation();

  const handleLogin = async () => {
    if (!That's why email. Because of that, trim() || ! Here's the thing — password. trim()) {
      Alert.alert('Validation Error', 'Please fill in both fields.

    setLoading(true);
    try {
      const response = await api.post('/auth/login', {
        email: email.trim(),
        password,
      });

      // Assuming the API returns { token: 'jwt-string', user: { ... } }
      const { token, user } = response.data;

      // Store token securely
      await AsyncStorage.setItem('userToken', token);
      // Optionally store user info
      await AsyncStorage.setItem('userInfo', JSON.

      // figure out to home or dashboard
      navigation.replace('Home');
    } catch (err) {
      let message = 'Login failed. Please try again.';
      if (err.response && err.Practically speaking, response. data && err.response.In practice, data. message) {
        message = err.response.data.message;
      }
      Alert.

  return (
    
      Login to Your App

      

      

      {loading ? (
        
      ) : (
        

Explanation of important parts:

  • State management: useState tracks email, password

Enhancing the Login Experience

The snippet you saw gives you a functional login screen, but most production‑grade applications demand a few extra layers of polish and protection. Below are several practical enhancements you can apply to the existing component without rewriting the whole file Simple as that..

1. dependable Input Validation

While the current check prevents empty fields, a login form should also verify that the email address follows a proper format and that the password meets any required strength criteria. Leveraging a lightweight validation library such as Yup or zod keeps the logic declarative and testable:

// Example validation schema (not part of the component)
const loginSchema = Yup.object().shape({
  email: Yup.string()
    .email('Invalid email address')
    .required('Email is required'),
  password: Yup.string()
    .min(8, 'Password must be at least 8 characters')
    .required('Password is required'),
});

You can integrate this schema into handleLogin by running loginSchema.validate({ email, password }, { abortEarly: false }). If validation fails, you can surface each error message next to its respective input, giving users immediate feedback rather than a generic alert.

2. Secure Token Storage

AsyncStorage is convenient, but it stores data in plain text on the device. For apps that handle sensitive authentication tokens, consider using react‑native‑keychain or expo‑secure‑store. These libraries encrypt the stored value and can enforce biometric prompts (Face ID, Touch ID) on iOS and biometric authentication on Android.

Most guides skip this. Don't.

import * as Keychain from 'react-native-keychain';

const storeToken = async (token) => {
  try {
    await Keychain.setInternetCredentials('myApp', 'authToken', token);
  } catch (error) {
    console.error('Failed to store token securely', error);
  }
};

When you retrieve the token later, you can also enforce that it’s stored with the proper access restrictions (e., ACCESSIBLE.Even so, g. WHEN_UNLOCKED_THIS_DEVICE_ONLY) Worth knowing..

3. Comprehensive Error Handling

The current catch block extracts a server‑provided message, but network‑level failures (offline mode, 5xx errors, malformed JSON) also need graceful treatment. You can augment the error handling like this:

const handleLogin = async () => {
  // validation omitted for brevity
  setLoading(true);
  try {
    const response = await api.post('/auth/login', payload);
    // success flow
  } catch (err) {
    if (!err.response) {
      // No response – likely a network issue
      Alert.alert('Network Error', 'Please check your internet connection.');
    } else {
      const message = err.response?.data?.message ?? 'Login failed.';
      Alert.alert('Authentication Failed',

message);
    }
  } finally {
    setLoading(false);
  }
};

In production, you may prefer inline error messages instead of Alert.alert, especially for repeated login attempts. Inline feedback is easier to style, more accessible, and avoids interrupting the user with modal popups.

4. Prevent Duplicate Submissions

Users may tap the login button multiple times while waiting for a response. Disabling the button while the request is in progress prevents duplicate submissions and reduces unnecessary load on the authentication endpoint.


  
    {loading ? 'Logging in...' : 'Login'}
  

You can also improve keyboard behavior by setting returnKeyType="done" on the password field and submitting the form when the user presses Enter.

5. Centralize Authentication State

For small apps, keeping the token in the login component may be acceptable. As the app grows, authentication state should usually be managed in a shared context, store, or auth provider. This prevents scattered token checks and makes it easier to redirect users, refresh sessions, and log them out globally.

No fluff here — just what actually works.

Here's one way to look at it: an auth context can expose values such as:

const auth = {
  user,
  token,
  isAuthenticated,
  login,
  logout,
  loading,
};

The login screen then only needs to collect credentials and call auth.login(email, password), while the rest of the app consumes the same authentication state That's the whole idea..

6. Add Basic Tests

A login screen is a critical user flow, so it should be covered by at least a few focused tests. Useful cases include:

  • Preventing submission with empty fields
  • Showing validation errors for invalid email formats
  • Displaying loading state during login
  • Handling failed API responses
  • Storing the token after a successful login

With React Native Testing Library, a simple test might look like this:

import { render, fireEvent, screen } from '@testing-library/react-native';
import LoginScreen from '../LoginScreen';

test('shows validation error when email is empty', async () => {
  render();

  fireEvent.changeText(screen.getByPlaceholderText(/password/i), 'password123');
  fireEvent.press(screen.getByText(/login/i));

  expect(await screen.findByText(/email is required/i)).toBeTruthy();
});

Testing these basic scenarios helps catch regressions when the login flow changes later.

Conclusion

The existing login implementation provides a solid starting point, but production-ready authentication requires a bit more care. By adding structured validation, secure token storage, stronger error handling, duplicate-submit protection

, and centralized state management, you can build a login flow that is both user-friendly and solid. Because of that, these practices not only improve the immediate user experience but also lay a foundation for scalability and maintainability as your application evolves. Remember that authentication is a critical component of any app, and investing time in these details will pay off in reduced bugs, better security, and easier feature expansion down the road. By following these steps, you see to it that your login system is ready for the complexities of real-world usage.

Just Came Out

Hot Off the Blog

Explore the Theme

Follow the Thread

Thank you for reading about React Native Login Api Example Axios. We hope the information has been useful. Feel free to contact us if you have any questions. See you next time — don't forget to bookmark!
⌂ Back to Home