useUserinfo

The useUserinfo hook is a React hook attached to the auth client that provides access to user information with reactive state management. It automatically fetches user info on mount and provides loading, error, and refetching states powered by Zustand.

This hook is accessed via authClient.useUserinfo() and is ideal when you need detailed user information with comprehensive state management.


Hook Signature

useUserinfo(options?: {
  query?: {
    enabled?: boolean;
  }
}): {
  data: User | null;
  isPending: boolean;
  isRefetching: boolean;
  error: Error | null;
  refetch: () => Promise<void>;
}

Parameters

options (optional)

  • type: object
  • Configuration options for the hook

options.query.enabled

  • type: boolean
  • Default: true
  • Whether to automatically fetch user info on mount

Return Values

The hook returns an object with the following properties:

data

  • type: User | null
  • The current user information object
  • null if not authenticated or not yet loaded

isPending

  • type: boolean
  • Indicates whether the initial user info fetch is in progress
  • true during the first fetch, false after completion

isRefetching

  • type: boolean
  • Indicates whether a refetch is in progress (after initial load)
  • Useful for showing "refreshing" UI without hiding existing data

error

  • type: Error | null
  • Error object if the fetch failed
  • null if no error occurred

refetch

  • type: () => Promise<void>
  • Function to manually refetch user information
  • Sets isRefetching to true during the refetch

User Object Structure

interface User {
  sub: string;              // Unique user identifier (subject)
  name?: string;            // Full name
  email?: string;           // Email address
  picture?: string;         // Profile picture URL
  given_name?: string;      // First name
  family_name?: string;     // Last name
  email_verified?: string;  // Email verification status
}

How It Works

  1. On mount: If enabled !== false, automatically calls client.getUserinfo()
  2. State management: Uses Zustand store for efficient React state updates
  3. Loading states: Provides separate isPending (initial) and isRefetching (subsequent) states
  4. Error handling: Catches and stores errors in the error state
  5. Manual refresh: Provides refetch function for manual updates

Usage

Basic Usage

import { authClient } from "@/auth";

function Profile() {
  const { data: user, isPending, error, refetch } = authClient.useUserinfo();

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

  return (
    <div>
      <h2>Welcome {user.name}</h2>
      <p>Email: {user.email}</p>
      <button onClick={refetch}>Refresh</button>
    </div>
  );
}

With Loading States

Handle both initial loading and refetching states separately:

import { authClient } from "@/auth";

function Profile() {
  const { data: user, isPending, isRefetching, refetch } = authClient.useUserinfo();

  if (isPending) {
    return <div>Loading profile...</div>;
  }

  if (!user) {
    return <div>Not authenticated</div>;
  }

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

      <button onClick={refetch} disabled={isRefetching}>
        {isRefetching ? "Refreshing..." : "Refresh"}
      </button>
    </div>
  );
}

Disable Auto-Fetch

Disable automatic fetching on mount and fetch manually:

import { authClient } from "@/auth";

function Profile() {
  const { data: user, refetch, isPending } = authClient.useUserinfo({
    query: { enabled: false },
  });

  return (
    <div>
      <button onClick={refetch} disabled={isPending}>
        {isPending ? "Loading..." : "Load Profile"}
      </button>

      {user && (
        <div>
          <h2>{user.name}</h2>
          <p>{user.email}</p>
        </div>
      )}
    </div>
  );
}

Complete Example with Error Handling

import { authClient } from "@/auth";

function UserProfile() {
  const { data: user, isPending, isRefetching, error, refetch } = authClient.useUserinfo();

  if (isPending) {
    return (
      <div className="loading">
        <Spinner />
        <p>Loading your profile...</p>
      </div>
    );
  }

  if (error) {
    return (
      <div className="error">
        <h3>Failed to load profile</h3>
        <p>{error.message}</p>
        <button onClick={refetch}>Try Again</button>
      </div>
    );
  }

  if (!user) {
    return (
      <div className="not-authenticated">
        <p>Please sign in to view your profile</p>
        <button onClick={() => authClient.signIn({ authMode: AuthMode.Popup, callbackUrl: "/" })}>
          Sign In
        </button>
      </div>
    );
  }

  return (
    <div className="profile">
      <div className="profile-header">
        {user.picture && <img src={user.picture} alt={user.name} />}
        <h2>{user.name}</h2>
        <p>{user.email}</p>
      </div>

      <div className="profile-details">
        {user.given_name && <p>First name: {user.given_name}</p>}
        {user.family_name && <p>Last name: {user.family_name}</p>}
        {user.email_verified && <p>Email verified: {user.email_verified}</p>}
        <p>User ID: {user.sub}</p>
      </div>

      <button onClick={refetch} disabled={isRefetching}>
        {isRefetching ? "Refreshing..." : "Refresh Profile"}
      </button>
    </div>
  );
}

Refetch After Profile Update

import { authClient } from "@/auth";
import { useState } from "react";

function EditProfile() {
  const { data: user, refetch, isRefetching } = authClient.useUserinfo();
  const [name, setName] = useState(user?.name || "");

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();

    try {
      // Update profile on server
      await authClient.$fetch("/api/profile", {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ name }),
      });

      // Refetch user info to get updated data
      await refetch();

      alert("Profile updated successfully!");
    } catch (error) {
      console.error("Failed to update profile:", error);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Name"
      />
      <button type="submit" disabled={isRefetching}>
        Save
      </button>
    </form>
  );
}

Conditional Fetch Based on Auth State

import { authClient } from "@/auth";

function ConditionalProfile() {
  const { isAuthenticated } = authClient.useAuth();

  // Only fetch if authenticated
  const { data: user, isPending } = authClient.useUserinfo({
    query: { enabled: isAuthenticated },
  });

  if (!isAuthenticated) {
    return <div>Please sign in</div>;
  }

  if (isPending) {
    return <div>Loading...</div>;
  }

  return <div>Welcome, {user?.name}!</div>;
}

State Management

The hook uses Zustand for efficient state management:

  • Minimal re-renders: Only subscribes to the specific state slices you need
  • Shared state: Multiple components using the hook share the same state
  • Automatic cleanup: State is managed efficiently by Zustand

Example of shared state:

// Both components share the same user state
function Header() {
  const { data: user } = authClient.useUserinfo();
  return <div>{user?.name}</div>;
}

function Sidebar() {
  const { data: user, refetch } = authClient.useUserinfo();
  return (
    <div>
      <p>{user?.email}</p>
      <button onClick={refetch}>Refresh</button>
    </div>
  );
}

// When refetch is called in Sidebar, Header automatically updates

Comparison with useAuth

FeatureuseUserinfouseAuth
User data
Loading state✓ (isPending)✓ (isPending)
Refetching state
Error handling
Refetch function
Disable auto-fetch
isAuthenticated flag
signIn method
signOut method
Best forDetailed user infoSimple auth checks

When to Use

Use useUserinfo when:

  • You need detailed error handling
  • You want manual refetch capability
  • You need separate isPending and isRefetching states
  • You want to disable auto-fetch on mount
  • You're building a profile page or user dashboard

Use useAuth instead when:

  • You need quick authentication status checks
  • You want built-in signIn/signOut methods
  • You're building login/logout buttons
  • You need a simple isAuthenticated flag

Performance Considerations

  • Efficient re-renders: Only components that subscribe to changed state slices re-render
  • Shared state: Multiple components share the same data without duplicate fetches
  • Automatic caching: User info is cached in Zustand store
  • No unnecessary fetches: Set enabled: false to prevent auto-fetch when not needed

Error Handling

The hook handles errors gracefully:

  • Network errors: Caught and stored in the error state
  • 401 Unauthorized: Token is automatically cleared by getUserinfo()
  • Console logging: Errors are logged to console for debugging
  • No crashes: Errors don't crash the component, they're stored in state

Always check for errors in your component:

const { data, error } = authClient.useUserinfo();

if (error) {
  // Handle error appropriately
  return <ErrorMessage error={error} />;
}

See Also