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
nullif not authenticated or not yet loaded
isPending
- type:
boolean- Indicates whether the initial user info fetch is in progress
trueduring the first fetch,falseafter 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
nullif no error occurred
refetch
- type:
() => Promise<void>- Function to manually refetch user information
- Sets
isRefetchingtotrueduring 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
- On mount: If
enabled !== false, automatically callsclient.getUserinfo() - State management: Uses Zustand store for efficient React state updates
- Loading states: Provides separate
isPending(initial) andisRefetching(subsequent) states - Error handling: Catches and stores errors in the
errorstate - Manual refresh: Provides
refetchfunction 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
| Feature | useUserinfo | useAuth |
|---|---|---|
| User data | ✓ | ✓ |
| Loading state | ✓ (isPending) | ✓ (isPending) |
| Refetching state | ✓ | ✗ |
| Error handling | ✓ | ✗ |
| Refetch function | ✓ | ✗ |
| Disable auto-fetch | ✓ | ✗ |
| isAuthenticated flag | ✗ | ✓ |
| signIn method | ✗ | ✓ |
| signOut method | ✗ | ✓ |
| Best for | Detailed user info | Simple 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: falseto prevent auto-fetch when not needed
Error Handling
The hook handles errors gracefully:
- Network errors: Caught and stored in the
errorstate - 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
- useAuth hook - Simpler hook for authentication state
- getUserinfo function - Imperative API for fetching user info
- Basic Usage - Getting started guide