> ## Documentation Index
> Fetch the complete documentation index at: https://auth0.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Add Login to Your React Application

> This guide demonstrates how to integrate Auth0, add authentication, and display user profile information in a Single-Page Application (SPA) that uses React, using the Auth0 React SDK.

export const HowToSchema = () => <script type="application/ld+json">
    {'{"@context":"https://schema.org","@type":"HowTo"}'}
  </script>;

export const CreateInteractiveApp = ({placeholderText = 'Auth0', appType = 'regular_web', allowedCallbackUrls = ['localhost:3000'], allowedLogoutUrls = ['localhost:3000'], allowedOriginUrls = ['localhost:3000']}) => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [storeReady, setStoreReady] = useState(false);
  const [displayForm, setDisplayForm] = useState(true);
  useEffect(() => {
    const init = () => setStoreReady(true);
    if (window.rootStore) {
      window.rootStore.clientStore.setSelectedClient(null);
      window.rootStore.clientStore.setSelectedClientSecret(undefined);
      init();
    } else {
      window.addEventListener('adu:storeReady', init);
    }
    return () => {
      window.removeEventListener('adu:storeReady', init);
    };
  }, []);
  useEffect(() => {
    if (!storeReady) return;
    const disposer = autorun(() => {
      const rootStore = window.rootStore;
      setIsAuthenticated(rootStore.sessionStore.isAuthenticated);
    });
    return () => {
      disposer();
    };
  }, [storeReady]);
  if (!storeReady || typeof window === 'undefined' || !displayForm) {
    return <></>;
  }
  const login = () => {
    const baseUrl = window.rootStore.config.apiBaseUrl;
    const returnTo = encodeURIComponent(window.location.href);
    window.location.href = `${baseUrl}/auth/user/login?returnTo=${returnTo}`;
  };
  const Card = ({className = '', children}) => {
    return <div className={`
          flex border rounded-2xl
          border-gray-950/10 dark:border-white/10
          py-3.5 px-4 gap-2
          text-sm text-gray-900 dark:text-gray-200
          ${className}
        `}>
        {children}
      </div>;
  };
  const Button = ({children, ...props}) => {
    return <button className="bg-[--button-primary] text-[--foreground-inverse] px-[1.125rem] py-1.5 rounded-lg font-medium" {...props}>
        {children}
      </button>;
  };
  const CreateApplicationForm = () => {
    const [name, setName] = useState('');
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState('');
    const handleSubmit = async () => {
      if (!name.trim()) {
        setError('Application name is required');
        return;
      }
      setIsLoading(true);
      setError(null);
      try {
        await window.rootStore.clientStore.createClient({
          name: name.trim(),
          app_type: appType,
          callbacks: allowedCallbackUrls,
          allowed_logout_urls: allowedLogoutUrls,
          web_origins: allowedOriginUrls,
          client_metadata: {
            created_by: 'quickstart-docs-app-creation-component'
          }
        });
        setDisplayForm(false);
      } catch (err) {
        console.error('Error creating client:', err);
        const errorMessage = err instanceof Error ? err.message : 'Failed to create application';
        setError(errorMessage);
      } finally {
        setIsLoading(false);
      }
    };
    return <Card className="flex-col items-start p-4 gap-3.75">
        <span className="font-medium text-gray-900 dark:text-gray-200">
          Create Auth0 App
        </span>
        <div className="w-full flex gap-2">
          <input id="app-name" name={name} className="
              w-full max-w-[448px] h-11 py-2 px-4 
              border rounded-lg border-gray-950/10 dark:border-white/10 
              text-gray-900 dark:text-gray-200
              focus:outline-none dark:focus:outline-none
            " placeholder={`My ${placeholderText} App`} value={name} onChange={e => setName(e.target.value)} />
          <Button onClick={handleSubmit}>
            {isLoading ? 'Creating...' : 'Create'}
          </Button>
        </div>
        {error && <p className="text-red-500">{error}</p>}
      </Card>;
  };
  const SignInForm = () => {
    return <Card className="items-center">
        <Button onClick={login}>Log in</Button> <span>to create the app</span>
      </Card>;
  };
  return isAuthenticated ? <CreateApplicationForm /> : <SignInForm />;
};

export const AuthCodeBlock = ({filename, icon, language, highlight, children}) => {
  const [displayText, setDisplayText] = useState(children);
  const [copyText, setCopyText] = useState(children);
  const wrapperRef = React.useRef(null);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      if (!window.autorun || !window.rootStore) {
        return;
      }
      unsubscribe = window.autorun(() => {
        let processedChildrenForDisplay = children;
        let processedChildrenForCopy = children;
        for (const [key, value] of window.rootStore.variableStore.values.entries()) {
          const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
          let displayValue = value;
          if (key === "{yourClientSecret}" && value !== "{yourClientSecret}") {
            displayValue = value.substring(0, 3) + "*****MASKED*****";
          }
          processedChildrenForDisplay = processedChildrenForDisplay.replaceAll(new RegExp(escapedKey, "g"), displayValue);
          processedChildrenForCopy = processedChildrenForCopy.replaceAll(new RegExp(escapedKey, "g"), value);
        }
        setDisplayText(processedChildrenForDisplay);
        setCopyText(processedChildrenForCopy);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  useEffect(() => {
    if (!wrapperRef.current) return;
    const originalWriteText = navigator.clipboard.writeText.bind(navigator.clipboard);
    let isOverriding = false;
    const handleClick = e => {
      const button = e.target.closest('[data-testid="copy-code-button"]');
      if (!button || !wrapperRef.current.contains(button)) return;
      isOverriding = true;
      navigator.clipboard.writeText = text => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
          return originalWriteText(copyText);
        }
        return originalWriteText(text);
      };
      setTimeout(() => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
        }
      }, 100);
    };
    const wrapper = wrapperRef.current;
    wrapper.addEventListener('click', handleClick, true);
    return () => {
      wrapper.removeEventListener('click', handleClick, true);
      if (navigator.clipboard.writeText !== originalWriteText) {
        navigator.clipboard.writeText = originalWriteText;
      }
    };
  }, [copyText]);
  return <div ref={wrapperRef}>
      <CodeBlock filename={filename} icon={icon} language={language} lines highlight={highlight}>
        {displayText}
      </CodeBlock>
    </div>;
};

export const AuthCodeGroup = ({children, dropdown}) => {
  const [processedChildren, setProcessedChildren] = useState(children);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      unsubscribe = window.autorun(() => {
        const processChildren = node => {
          if (typeof node === "string") {
            let processedNode = node;
            for (const [key, value] of window.rootStore.variableStore.values.entries()) {
              const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
              processedNode = processedNode.replaceAll(new RegExp(escapedKey, "g"), value);
            }
            return processedNode;
          } else if (Array.isArray(node)) {
            return node.map(processChildren);
          } else if (node && node.props && node.props.children) {
            return {
              ...node,
              props: {
                ...node.props,
                children: processChildren(node.props.children)
              }
            };
          }
          return node;
        };
        setProcessedChildren(processChildren(children));
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  return <CodeGroup dropdown={dropdown}>{processedChildren}</CodeGroup>;
};

<HowToSchema />

<Accordion title="Use AI to integrate Auth0" icon="microchip-ai" iconType="solid" defaultOpen>
  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](https://agentskills.io/home).

  **Install:**

  ```bash theme={null}
  npx skills add auth0/agent-skills --skill auth0-quickstart --skill auth0-react
  ```

  **Then ask your AI assistant:**

  ```text theme={null}
  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 →](/docs/quickstart/agent-skills)
</Accordion>

<Note>
  **Prerequisites:** Before you begin, ensure you have the following installed:

  * **[Node.js](https://nodejs.org/en/download)** 20 LTS or newer
  * **[npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)** 10+ or **[yarn](https://classic.yarnpkg.com/lang/en/docs/install/)** 1.22+ or **[pnpm](https://pnpm.io/installation)** 8+

  Verify installation: `node --version && npm --version`

  **React Version Compatibility:** This quickstart works with **React 18.x** and newer.
</Note>

## 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.

export const localEnvSnippet = `VITE_AUTH0_DOMAIN={yourDomain}
VITE_AUTH0_CLIENT_ID={yourClientId}`;

<Steps>
  <Step title="Create a new project" stepNumber={1}>
    Create a new React project for this Quickstart

    ```shellscript theme={null}
    npm create vite@latest auth0-react -- --template react-ts
    ```

    Open the project

    ```shellscript theme={null}
    cd auth0-react
    ```
  </Step>

  <Step title="Install the Auth0 React SDK" stepNumber={2}>
    ```shellscript theme={null}
    npm add @auth0/auth0-react && npm install
    ```
  </Step>

  <Step title="Setup your Auth0 App" stepNumber={3}>
    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:

    <Tabs>
      <Tab title="Quick Setup (recommended)">
        Create an Auth0 App and copy the pre-filled `.env` file with the right configuration values.

        <CreateInteractiveApp placeholderText="React" appType="spa" allowedCallbackUrls={["http://localhost:5173"]} allowedLogoutUrls={["http://localhost:5173"]} allowedOriginUrls={["http://localhost:5173"]} />

        <AuthCodeBlock children={localEnvSnippet} language="shellscript" filename=".env" />
      </Tab>

      <Tab title="CLI">
        Run the following command in your project's root directory to create an Auth0 app and generate a `.env` file:

        <CodeGroup>
          ```shellscript Mac theme={null}
          # 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
          ```

          ```powershell Windows theme={null}
          # 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
          ```
        </CodeGroup>

        <Note>
          This command will:

          1. Check if you're authenticated (and prompt for login if needed)
          2. Create an Auth0 Single Page Application configured for `http://localhost:5173`
          3. Generate a `.env` file with `VITE_AUTH0_DOMAIN` and `VITE_AUTH0_CLIENT_ID`
        </Note>
      </Tab>

      <Tab title="Dashboard">
        Before you start, create a `.env` file on your project's root directory

        ```shellscript .env theme={null}
        VITE_AUTH0_DOMAIN=YOUR_AUTH0_APP_DOMAIN
        VITE_AUTH0_CLIENT_ID=YOUR_AUTH0_APP_CLIENT_ID
        ```

        1. Head to the [Auth0 Dashboard](https://manage.auth0.com/dashboard/)
        2. Click on **Applications** > **Applications** > **Create Application**
        3. In the popup, enter a name for your app, select `Single Page Web Application` as the app type and click **Create**
        4. Switch to the **Settings** tab on the Application Details page
        5. 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
        ```

        <Info>
          **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.
        </Info>
      </Tab>
    </Tabs>

    <Tip>
      Verify your `.env` file exists: `cat .env` (Mac/Linux) or `type .env` (Windows)
    </Tip>
  </Step>

  <Step title="Add the provider" stepNumber={4}>
    ```javascript src/main.tsx {5,9,10,11,12,13,14,15,17} lines theme={null}
    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>
    );
    ```
  </Step>

  <Step title="Create Login, Logout and Profile Components" stepNumber={5}>
    Create files

    <CodeGroup>
      ```shellscript Mac/Linux theme={null}
      touch src/LoginButton.tsx && touch src/LogoutButton.tsx && touch src/Profile.tsx
      ```

      ```powershell Windows theme={null}
      New-Item -ItemType File -Path src/LoginButton.tsx
      New-Item -ItemType File -Path src/LogoutButton.tsx
      New-Item -ItemType File -Path src/Profile.tsx
      ```
    </CodeGroup>

    And add the following code snippets

    <AuthCodeGroup>
      ```javascript src/LoginButton.tsx lines theme={null}
      import { useAuth0 } from "@auth0/auth0-react";

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

      export default LoginButton;
      ```

      ```javascript src/LogoutButton.tsx lines theme={null}
      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;
      ```

      ```javascript src/Profile.tsx expandable lines theme={null}
      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 as HTMLImageElement;
                  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;
      ```

      ```javascript src/App.tsx expandable lines theme={null}
      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;
      ```

      ```css src/index.css expandable lines theme={null}
      @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');

      body {
        margin: 0;
        font-family: 'Inter', sans-serif;
        background-color: #1a1e27;
        min-height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
        color: #e2e8f0;
        overflow: hidden;
      }

      #root {
        width: 100%;
        height: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
      }

      .app-container {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        min-height: 100vh;
        width: 100%;
        padding: 1rem;
        box-sizing: border-box;
      }

      .loading-state, .error-state {
        background-color: #2d313c;
        border-radius: 15px;
        box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
        padding: 3rem;
        text-align: center;
      }

      .loading-text {
        font-size: 1.8rem;
        font-weight: 500;
        color: #a0aec0;
        animation: pulse 1.5s infinite ease-in-out;
      }

      .error-state {
        background-color: #c53030;
        color: #fff;
      }

      .error-title {
        font-size: 2.8rem;
        font-weight: 700;
        margin-bottom: 0.5rem;
      }

      .error-message {
        font-size: 1.3rem;
        margin-bottom: 0.5rem;
      }

      .error-sub-message {
        font-size: 1rem;
        opacity: 0.8;
      }

      .main-card-wrapper {
        background-color: #262a33;
        border-radius: 20px;
        box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05);
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 2rem;
        padding: 3rem;
        max-width: 500px;
        width: 90%;
        animation: fadeInScale 0.8s ease-out forwards;
      }

      .auth0-logo {
        width: 160px;
        margin-bottom: 1.5rem;
        opacity: 0;
        animation: slideInDown 1s ease-out forwards 0.2s;
      }

      .main-title {
        font-size: 2.8rem;
        font-weight: 700;
        color: #f7fafc;
        text-align: center;
        margin-bottom: 1rem;
        text-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
        opacity: 0;
        animation: fadeIn 1s ease-out forwards 0.4s;
      }

      .action-card {
        background-color: #2d313c;
        border-radius: 15px;
        box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.3), 0 5px 15px rgba(0, 0, 0, 0.3);
        padding: 2.5rem;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 1.8rem;
        width: calc(100% - 2rem);
        opacity: 0;
        animation: fadeIn 1s ease-out forwards 0.6s;
      }

      .action-text {
        font-size: 1.25rem;
        color: #cbd5e0;
        text-align: center;
        line-height: 1.6;
        font-weight: 400;
      }

      .button {
        padding: 1.1rem 2.8rem;
        font-size: 1.2rem;
        font-weight: 600;
        border-radius: 10px;
        border: none;
        cursor: pointer;
        transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
        box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
        text-transform: uppercase;
        letter-spacing: 0.08em;
        outline: none;
      }

      .button:focus {
        box-shadow: 0 0 0 4px rgba(99, 179, 237, 0.5);
      }

      .button.login {
        background-color: #63b3ed;
        color: #1a1e27;
      }

      .button.login:hover {
        background-color: #4299e1;
        transform: translateY(-5px) scale(1.03);
        box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
      }

      .button.logout {
        background-color: #fc8181;
        color: #1a1e27;
      }

      .button.logout:hover {
        background-color: #e53e3e;
        transform: translateY(-5px) scale(1.03);
        box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
      }

      .logged-in-section {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 1.5rem;
        width: 100%;
      }

      .logged-in-message {
        font-size: 1.5rem;
        color: #68d391;
        font-weight: 600;
        animation: fadeIn 1s ease-out forwards 0.8s;
      }

      .profile-section-title {
        font-size: 2.2rem;
        animation: slideInUp 1s ease-out forwards 1s;
      }

      .profile-card {
        padding: 2.2rem;
        animation: scaleIn 0.8s ease-out forwards 1.2s;
      }

      .profile-picture {
        width: 110px;
        transition: transform 0.3s ease-in-out;
      }

      .profile-picture:hover {
        transform: scale(1.05);
      }

      .profile-name {
        font-size: 2rem;
        margin-top: 0.5rem;
      }

      .profile-email {
        font-size: 1.15rem;
        text-align: center;
      }

      @keyframes fadeIn {
        from { opacity: 0; }
        to { opacity: 1; }
      }

      @keyframes fadeInScale {
        from { opacity: 0; transform: scale(0.95); }
        to { opacity: 1; transform: scale(1); }
      }

      @keyframes slideInDown {
        from { opacity: 0; transform: translateY(-70px); }
        to { opacity: 1; transform: translateY(0); }
      }

      @keyframes slideInUp {
        from { opacity: 0; transform: translateY(50px); }
        to { opacity: 1; transform: translateY(0); }
      }

      @keyframes pulse {
        0%, 100% { opacity: 1; }
        50% { opacity: 0.6; }
      }

      @keyframes scaleIn {
        from { opacity: 0; transform: scale(0.8); }
        to { opacity: 1; transform: scale(1); }
      }

      @media (max-width: 600px) {
        .main-card-wrapper {
          padding: 2rem;
          margin: 1rem;
        }
        
        .main-title {
          font-size: 2.2rem;
        }
        
        .button {
          padding: 1rem 2rem;
          font-size: 1.1rem;
        }
        
        .auth0-logo {
          width: 120px;
        }
      }
      ```
    </AuthCodeGroup>
  </Step>

  <Step title="Run your app" stepNumber={7}>
    ```shellscript theme={null}
    npm run dev
    ```

    <Info>
      If port 5173 is in use, run: `npm run dev -- --port 5174` and update your Auth0 app's callback URLs to `http://localhost:5174`
    </Info>
  </Step>
</Steps>

<Check>
  **Checkpoint**

  You should now have a fully functional Auth0 login page running on your [localhost](http://localhost:5173/)
</Check>

***

## Advanced Usage

<Accordion title="Protecting Routes with React Router">
  Use Auth0's authentication state to protect specific routes in your application:

  ```jsx src/App.jsx theme={null}
  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>
    );
  }
  ```
</Accordion>

<Accordion title="Calling Protected APIs">
  Configure your Auth0Provider to include an API audience and use the `getAccessTokenSilently` method:

  ```jsx src/main.jsx theme={null}
  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:

  ```jsx src/components/ApiCall.jsx theme={null}
  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;
  ```
</Accordion>

<Accordion title="Using Auth0 with Custom Hooks">
  Create reusable custom hooks for common authentication patterns:

  ```jsx src/hooks/useAuthenticatedUser.js theme={null}
  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:

  ```jsx src/components/UserDashboard.jsx theme={null}
  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>
    );
  }
  ```
</Accordion>
