Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/auth0/nextjs-auth0/llms.txt

Use this file to discover all available pages before exploring further.

The useUser hook provides access to the authenticated user’s profile and session state in client components. It uses SWR (Stale-While-Revalidate) for efficient data fetching and caching.

Import

import { useUser } from '@auth0/nextjs-auth0';
This hook must be used in client components (marked with "use client" directive).

Signature

function useUser(): {
  user: User | null | undefined;
  isLoading: boolean;
  error: Error | null;
  invalidate: () => void;
}

Return Value

The hook returns an object with the following properties:
user
User | null | undefined
The authenticated user object, null if not authenticated, or undefined while loading.The User type includes:
  • sub - User ID (subject)
  • name - Full name
  • nickname - Nickname
  • given_name - First name
  • family_name - Last name
  • picture - Profile picture URL
  • email - Email address
  • email_verified - Email verification status
  • org_id - Organization ID (if logged in through an organization)
  • Additional custom claims
isLoading
boolean
true while the user data is being fetched, false otherwise.
error
Error | null
Error object if fetching user data failed, null otherwise.
invalidate
() => void
Function to manually trigger a revalidation of the user data.

Basic Usage

app/profile/page.tsx
"use client";

import { useUser } from '@auth0/nextjs-auth0';

export default function Profile() {
  const { user, isLoading, error } = useUser();

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!user) return <div>Not authenticated</div>;

  return (
    <div>
      <h1>Profile</h1>
      <div>
        <img src={user.picture} alt={user.name} />
        <h2>{user.name}</h2>
        <p>{user.email}</p>
      </div>
    </div>
  );
}

Usage with Auth0Provider

For optimal performance, wrap your application with Auth0Provider to provide initial user data:
app/layout.tsx
import { Auth0Provider } from '@auth0/nextjs-auth0';
import { auth0 } from '@/lib/auth0';

export default async function RootLayout({
  children
}: {
  children: React.ReactNode;
}) {
  const session = await auth0.getSession();

  return (
    <html lang="en">
      <body>
        <Auth0Provider user={session?.user}>
          {children}
        </Auth0Provider>
      </body>
    </html>
  );
}
This eliminates the initial loading state when the page first renders.

Manual Revalidation

You can manually trigger a refresh of user data using the invalidate function:
"use client";

import { useUser } from '@auth0/nextjs-auth0';

export default function ProfileEditor() {
  const { user, invalidate } = useUser();

  async function updateProfile(data) {
    await fetch('/api/profile', {
      method: 'POST',
      body: JSON.stringify(data)
    });
    
    // Refresh user data after update
    invalidate();
  }

  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      updateProfile({ name: e.target.name.value });
    }}>
      <input name="name" defaultValue={user?.name} />
      <button type="submit">Save</button>
    </form>
  );
}

Conditional Rendering

"use client";

import { useUser } from '@auth0/nextjs-auth0';

export default function Navigation() {
  const { user, isLoading } = useUser();

  if (isLoading) {
    return <nav>...</nav>;
  }

  return (
    <nav>
      {user ? (
        <>
          <a href="/profile">Profile</a>
          <a href="/auth/logout">Logout</a>
        </>
      ) : (
        <a href="/auth/login">Login</a>
      )}
    </nav>
  );
}

Error Handling

"use client";

import { useUser } from '@auth0/nextjs-auth0';

export default function UserStatus() {
  const { user, isLoading, error, invalidate } = useUser();

  if (isLoading) {
    return <div>Loading user data...</div>;
  }

  if (error) {
    return (
      <div>
        <p>Failed to load user: {error.message}</p>
        <button onClick={invalidate}>Retry</button>
      </div>
    );
  }

  if (!user) {
    return <a href="/auth/login">Please log in</a>;
  }

  return <p>Logged in as {user.email}</p>;
}

Understanding SWR Behavior

The useUser hook uses SWR (Stale-While-Revalidate) under the hood, which provides:
  • Event-driven revalidation: Data automatically revalidates when you:
    • Focus the browser tab
    • Reconnect to the internet
    • Mount the component
  • No background polling: The hook does not make continuous background requests unless explicitly configured
  • Cache-first approach: Returns cached data immediately, then revalidates if needed

Customizing SWR Behavior

While useUser doesn’t expose SWR configuration directly, you can control revalidation behavior globally via Auth0Provider:
app/layout.tsx
import { Auth0Provider } from '@auth0/nextjs-auth0';
import { SWRConfig } from 'swr';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <SWRConfig
          value={{
            revalidateOnFocus: false,
            revalidateOnReconnect: false,
            refreshInterval: 0 // Disable polling
          }}
        >
          <Auth0Provider>
            {children}
          </Auth0Provider>
        </SWRConfig>
      </body>
    </html>
  );
}

API Route

The useUser hook fetches data from the /auth/profile route (configurable via NEXT_PUBLIC_PROFILE_ROUTE environment variable). If you’re using custom routes, ensure your profile endpoint returns user data:
app/api/auth/profile/route.ts
import { auth0 } from '@/lib/auth0';
import { NextResponse } from 'next/server';

export async function GET() {
  const session = await auth0.getSession();
  
  if (!session) {
    return new NextResponse(null, { status: 204 });
  }
  
  return NextResponse.json(session.user);
}

TypeScript

import { useUser } from '@auth0/nextjs-auth0';
import type { User } from '@auth0/nextjs-auth0';

interface ExtendedUser extends User {
  customClaim: string;
}

export default function Profile() {
  const { user } = useUser();
  
  // Type assertion for custom claims
  const extendedUser = user as ExtendedUser;
  
  return <div>{extendedUser?.customClaim}</div>;
}

Custom Environment Variables

You can customize the profile API route:
.env.local
# Default: /auth/profile
NEXT_PUBLIC_PROFILE_ROUTE=/api/auth/profile

See Also

Build docs developers (and LLMs) love