Add Login to Your React Application - Auth0 Docs

Use AI to integrate Auth0

If you use an AI coding assistant like Claude Code, Cursor, or GitHub Copilot, you can add Auth0 authentication automatically in minutes using agent skills.Install:

npx skills add auth0/agent-skills --skill auth0

Then ask your AI assistant:

Add Auth0 authentication to my React app

Your AI assistant will automatically create your Auth0 application, fetch credentials, install @auth0/auth0-react, configure the Auth0Provider, and create all necessary components. Full agent skills documentation →

Prerequisites: Before you begin, ensure you have the following installed:

Verify installation: node --version && npm --versionReact Version Compatibility: This quickstart works with React 18.x and newer.

Get Started

This quickstart demonstrates how to add Auth0 authentication to a React application. You’ll build a secure single-page app with login, logout, and user profile features using the Auth0 React SDK.

  1. Create a new project

Create a new React project for this Quickstart

npm create vite@latest auth0-react -- --template react-ts

Open the project

   cd auth0-react
  1. Install the Auth0 React SDK
npm add @auth0/auth0-react && npm install
  1. Setup your Auth0 App

Next up, you need to create a new app on your Auth0 tenant and add the environment variables to your project. You have three options to set up your Auth0 app: use the Quick Setup tool (recommended), run a CLI command, or configure manually via the Dashboard:

Create an Auth0 App and copy the pre-filled .env file with the right configuration values.

Log into create the app

.env

VITE_AUTH0_DOMAIN={yourDomain}
VITE_AUTH0_CLIENT_ID={yourClientId}

Run the following command in your project’s root directory to create an Auth0 app and generate a .env file:

Mac

# Install Auth0 CLI (if not already installed)
brew tap auth0/auth0-cli && brew install auth0

# Set up Auth0 app and generate .env file
auth0 qs setup --app --type spa --framework react --build-tool vite --name "My App" --port 5173

Windows

# Install Auth0 CLI (if not already installed)
scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git
scoop install auth0

# Set up Auth0 app and generate .env file
auth0 qs setup --app --type spa --framework react --build-tool vite --name "My App" --port 5173

Before you start, create a .env file on your project’s root directory

.env

VITE_AUTH0_DOMAIN=YOUR_AUTH0_APP_DOMAIN
VITE_AUTH0_CLIENT_ID=YOUR_AUTH0_APP_CLIENT_ID
  1. Head to the Auth0 Dashboard
    1. Click on Applications > Applications > Create Application
    2. In the popup, enter a name for your app, select Single Page Web Application as the app type and click Create
    3. Switch to the Settings tab on the Application Details page
    4. Replace YOUR_AUTH0_APP_DOMAIN and YOUR_AUTH0_APP_CLIENT_ID on the .env file with the Domain and Client ID values from the dashboard

Finally, on the Settings tab of your Application Details page, configure the following URLs:Allowed Callback URLs:

http://localhost:5173

Allowed Logout URLs:

http://localhost:5173

Allowed Web Origins:

http://localhost:5173

Allowed Callback URLs are a critical security measure to ensure users are safely returned to your application after authentication. Without a matching URL, the login process will fail, and users will be blocked by an Auth0 error page instead of accessing your app. Allowed Logout URLs are essential for providing a seamless user experience upon signing out. Without a matching URL, users will not be redirected back to your application after logout and will instead be left on a generic Auth0 page. Allowed Web Origins is critical for silent authentication. Without it, users will be logged out when they refresh the page or return to your app later. Verify your .env file exists: cat .env (Mac/Linux) or type .env (Windows)

  1. Add the provider

src/main.tsx

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css'; // Importing the main CSS file
import App from './App.tsx';
import { Auth0Provider } from '@auth0/auth0-react';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <Auth0Provider
      domain={import.meta.env.VITE_AUTH0_DOMAIN}
      clientId={import.meta.env.VITE_AUTH0_CLIENT_ID}
      authorizationParams={{
        redirect_uri: window.location.origin
      }}
    >
      <App />
    </Auth0Provider>
  </StrictMode>
);
  1. Create Login, Logout and Profile Components

Create files

Mac/Linux

touch src/LoginButton.tsx && touch src/LogoutButton.tsx && touch src/Profile.tsx

Windows

New-Item -ItemType File -Path src/LoginButton.tsx
New-Item -ItemType File -Path src/LogoutButton.tsx
New-Item -ItemType File -Path src/Profile.tsx

And add the following code snippets:

src/LoginButton.tsx

import { useAuth0 } from "@auth0/auth0-react";

const LoginButton = () => {
  const { loginWithRedirect } = useAuth0();
  return (
    <button
      onClick={() => loginWithRedirect()}
      className="button login"
    >
      Log In
    </button>
  );
};

export default LoginButton;

src/LogoutButton.tsx

import { useAuth0 } from "@auth0/auth0-react";

const LogoutButton = () => {
  const { logout } = useAuth0();
  return (
    <button
      onClick={() => logout({ logoutParams: { returnTo: window.location.origin } })}
      className="button logout"
    >
      Log Out
    </button>
  );
};

export default LogoutButton;

src/Profile.tsx

import { useAuth0 } from "@auth0/auth0-react";

const Profile = () => {
  const { user, isAuthenticated, isLoading } = useAuth0();

if (isLoading) {
    return <div className="loading-text">Loading profile...</div>;
  }

return (
    isAuthenticated && user ? (
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1rem' }}>
        <img
          src={user.picture || `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='110' height='110' viewBox='0 0 110 110'%3E%3Ccircle cx='55' cy='55' r='55' fill='%2363b3ed'/%3E%3Cpath d='M55 50c8.28 0 15-6.72 15-15s-6.72-15-15-15-15 6.72-15 15 6.72 15 15 15zm0 7.5c-10 0-30 5.02-30 15v3.75c0 2.07 1.68 3.75 3.75 3.75h52.5c2.07 0 3.75-1.68 3.75-3.75V72.5c0-9.98-20-15-30-15z' fill='%23fff'/%3E%3C/svg%3E`}
          alt={user.name || 'User'}
          className="profile-picture"
          style={{
            width: '110px',
            height: '110px',
            borderRadius: '50%',
            objectFit: 'cover',
            border: '3px solid #63b3ed'
          }}
          onError={(e) => {
            const target = e.target;
            target.src = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='110' height='110' viewBox='0 0 110 110'%3E%3Ccircle cx='55' cy='55' r='55' fill='%2363b3ed'/%3E%3Cpath d='M55 50c8.28 0 15-6.72 15-15s-6.72-15-15-15-15 6.72-15 15 6.72 15 15 15zm0 7.5c-10 0-30 5.02-30 15v3.75c0 2.07 1.68 3.75 3.75 3.75h52.5c2.07 0 3.75-1.68 3.75-3.75V72.5c0-9.98-20-15-30-15z' fill='%23fff'/%3E%3C/svg%3E`;
          }}
        />
        <div style={{ textAlign: 'center' }}>
          <div className="profile-name" style={{ fontSize: '2rem', fontWeight: '600', color: '#f7fafc', marginBottom: '0.5rem' }}>
            {user.name}
          </div>
          <div className="profile-email" style={{ fontSize: '1.15rem', color: '#a0aec0' }}>
            {user.email}
          </div>
        </div>
      </div>
    ) : null
  );
};

export default Profile;

src/App.tsx

import { useAuth0 } from '@auth0/auth0-react';
import LoginButton from './LoginButton';
import LogoutButton from './LogoutButton';
import Profile from './Profile';

function App() {
  const { isAuthenticated, isLoading, error } = useAuth0();

if (isLoading) {
    return (
      <div className="app-container">
        <div className="loading-state">
          <div className="loading-text">Loading...</div>
        </div>
      </div>
    );
  }

if (error) {
    return (
      <div className="app-container">
        <div className="error-state">
          <div className="error-title">Oops!</div>
          <div className="error-message">Something went wrong</div>
          <div className="error-sub-message">{error.message}</div>
        </div>
      </div>
    );
  }

return (
    <div className="app-container">
      <div className="main-card-wrapper">
        <img
          src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-ondark.png"
          alt="Auth0 Logo"
          className="auth0-logo"
          onError={(e) => {
            e.currentTarget.style.display = 'none';
          }}
        />
        <h1 className="main-title">Welcome to Sample0</h1>

{isAuthenticated ? (
          <div className="logged-in-section">
            <div className="logged-in-message">✅ Successfully authenticated!</div>
            <h2 className="profile-section-title">Your Profile</h2>
            <div className="profile-card">
              <Profile />
            </div>
            <LogoutButton />
          </div>
        ) : (
          <div className="action-card">
            <p className="action-text">Get started by signing in to your account</p>
            <LoginButton />
          </div>
        )}
      </div>
    </div>
  );
}

export default App;
  1. Run your app
npm run dev

If port 5173 is in use, run: npm run dev -- --port 5174 and update your Auth0 app’s callback URLs to http://localhost:5174

Checkpoint You should now have a fully functional Auth0 login page running on your localhost

Advanced Usage

Protecting Routes with React Router

Use Auth0’s authentication state to protect specific routes in your application:

src/App.jsx

import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import { useAuth0 } from '@auth0/auth0-react';
import Profile from './components/Profile';
import Dashboard from './components/Dashboard';
import LoginButton from './components/LoginButton';

function ProtectedRoute({ children }) {
  const { isAuthenticated, isLoading } = useAuth0();

if (isLoading) return <div>Loading...</div>;

return isAuthenticated ? children : <Navigate to="/" />;
}

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<LoginButton />} />
        <Route
          path="/profile"
          element={
            <ProtectedRoute>
              <Profile />
            </ProtectedRoute>
          }
        />
        <Route
          path="/dashboard"
          element={
            <ProtectedRoute>
              <Dashboard />
            </ProtectedRoute>
          }
        />
      </Routes>
    </BrowserRouter>
  );
}

Calling Protected APIs

Configure your Auth0Provider to include an API audience and use the getAccessTokenSilently method:

src/main.jsx

import { Auth0Provider } from '@auth0/auth0-react';

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  <Auth0Provider
    domain={import.meta.env.VITE_AUTH0_DOMAIN}
    clientId={import.meta.env.VITE_AUTH0_CLIENT_ID}
    authorizationParams={{
      redirect_uri: window.location.origin,
      audience: "YOUR_API_IDENTIFIER"
    }}
  >
    <App />
  </Auth0Provider>
);

Then make authenticated API calls:

src/components/ApiCall.jsx

import { useState } from 'react';
import { useAuth0 } from '@auth0/auth0-react';

function ApiCall() {
  const { getAccessTokenSilently } = useAuth0();
  const [apiResponse, setApiResponse] = useState(null);

const callProtectedApi = async () => {
    try {
      const token = await getAccessTokenSilently();

const response = await fetch('/api/protected', {
        headers: {
          Authorization: `Bearer ${token}`
        }
      });

const data = await response.json();
      setApiResponse(data);
    } catch (error) {
      console.error('API call failed:', error);
    }
  };

return (
    <div>
      <button onClick={callProtectedApi}>Call API</button>
      {apiResponse && <pre>{JSON.stringify(apiResponse, null, 2)}</pre>}
    </div>
  );
}

export default ApiCall;

Using Auth0 with Custom Hooks

Create reusable custom hooks for common authentication patterns:

src/hooks/useAuthenticatedUser.js

import { useAuth0 } from '@auth0/auth0-react';
import { useEffect, useState } from 'react';

export function useAuthenticatedUser() {
  const { user, isAuthenticated, isLoading, getAccessTokenSilently } = useAuth0();
  const [accessToken, setAccessToken] = useState(null);

useEffect(() => {
    if (isAuthenticated && !isLoading) {
      getAccessTokenSilently()
        .then(token => setAccessToken(token))
        .catch(console.error);
    }
  }, [isAuthenticated, isLoading, getAccessTokenSilently]);

return {
    user,
    accessToken,
    isAuthenticated,
    isLoading
  };
}

Usage in components: src/components/UserDashboard.jsx

import { useAuthenticatedUser } from '../hooks/useAuthenticatedUser';

function UserDashboard() {
  const { user, accessToken, isLoading } = useAuthenticatedUser();

if (isLoading) return <div>Loading...</div>;

return (
    <div>
      <h1>Welcome, {user?.name}</h1>
      <p>Access Token: {accessToken ? 'Available' : 'Not available'}</p>
    </div>
  );
}