or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

backend-api-client.mdclient-components.mdclient-hooks.mderror-handling.mdindex.mdmiddleware-and-route-protection.mdserver-auth-app-router.mdserver-auth-pages-router.mdsetup-and-provider.mdwebhooks.md
tile.json

client-hooks.mddocs/

Client Hooks

React hooks for accessing authentication state, user data, and Clerk methods on the client side. All hooks are client-side only and require the 'use client' directive.

Key Information for Agents

Required Setup:

  • All hooks require 'use client' directive (client components only)
  • ClerkProvider must wrap application for hooks to work
  • Hooks access authentication state from ClerkProvider context

Default Behaviors:

  • useAuth() returns userId, sessionId, orgId as null when signed out, undefined when loading
  • isLoaded and isSignedIn properties indicate loading and authentication state
  • getToken() returns null if no session, Promise<string | null> for token retrieval
  • has() function checks permissions/roles synchronously (returns boolean)
  • useUser() and useSession() return user/session as null when signed out, undefined when loading
  • Organization hooks return null when no active organization
  • Sign-in/sign-up hooks return undefined when not initialized

Threading Model:

  • All hooks execute in React render cycle (browser/client context)
  • Hooks are reactive to auth state changes (re-render on auth state updates)
  • getToken() is async and must be awaited
  • signOut(), setActive(), and other mutation methods are async
  • Permission checks via has() are synchronous

Lifecycle:

  • Hooks initialize on component mount
  • Auth state is fetched from ClerkProvider context
  • Hooks subscribe to auth state changes automatically
  • Hooks clean up subscriptions on unmount
  • Token retrieval via getToken() fetches fresh token (can be cached)

Edge Cases:

  • userId is undefined during initial load, null when signed out
  • isLoaded: false means auth state is still loading (show loading UI)
  • isSignedIn: false with isLoaded: true means user is signed out
  • getToken() can return null if session invalid or expired
  • has() returns false if no active organization (even with permission)
  • Organization hooks return null organization when no active org
  • Sign-in/sign-up hooks require isLoaded check before using signIn/signUp objects
  • useSessionList() returns empty array when no sessions

Exceptions:

  • Missing ClerkProvider causes hooks to throw initialization errors
  • Calling hooks outside React component causes React hooks violation error
  • getToken() throws if Clerk not initialized
  • Mutation methods throw if called when not authenticated

Capabilities

useAuth Hook

Returns the current authentication state including user ID, session ID, organization data, and token management.

/**
 * Returns authentication state and utilities
 * @returns Authentication object with user/session IDs and methods
 */
function useAuth(): UseAuthReturn;

interface UseAuthReturn {
  /** Current user ID, null if signed out, undefined if loading */
  userId: string | null | undefined;
  /** Current session ID, null if signed out, undefined if loading */
  sessionId: string | null | undefined;
  /** Current organization ID */
  orgId: string | null | undefined;
  /** Current user's role in the active organization */
  orgRole: string | null | undefined;
  /** Current organization slug */
  orgSlug: string | null | undefined;
  /** Current user's permissions in the active organization */
  orgPermissions: string[] | null | undefined;
  /** Actor identifier for impersonation scenarios */
  actor: any | null | undefined;
  /** Check if user has a specific permission */
  has: (permission: string | { permission: string } | { role: string }) => boolean;
  /** Get session token (promisified for Next.js) */
  getToken: (options?: GetTokenOptions) => Promise<string | null>;
  /** Sign out the current user */
  signOut: () => Promise<void>;
  /** True if authentication state is loaded */
  isLoaded: boolean;
  /** True if user is signed in */
  isSignedIn: boolean;
}

interface GetTokenOptions {
  /** Template name for custom JWT templates */
  template?: string;
  /** Skip cache and fetch fresh token */
  skipCache?: boolean;
}

Usage Example:

'use client';

import { useAuth } from '@clerk/nextjs';

export default function Component() {
  const { userId, isLoaded, isSignedIn, getToken, has } = useAuth();

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

  if (!isSignedIn) {
    return <div>Not signed in</div>;
  }

  const handleGetToken = async () => {
    const token = await getToken();
    // Use token for API requests
  };

  const isAdmin = has({ role: 'admin' });

  return <div>User ID: {userId}</div>;
}

useUser Hook

Returns the current User object with profile data and update methods.

/**
 * Returns the current User object
 * @returns User object and loading state
 */
function useUser(): UseUserReturn;

interface UseUserReturn {
  /** True if user data is loaded */
  isLoaded: boolean;
  /** True if user is signed in */
  isSignedIn: boolean;
  /** User object, null if signed out, undefined if loading */
  user: User | null | undefined;
}

interface User {
  /** User's unique identifier */
  id: string;
  /** User's email addresses */
  emailAddresses: EmailAddress[];
  /** User's phone numbers */
  phoneNumbers: PhoneNumber[];
  /** User's external OAuth accounts */
  externalAccounts: ExternalAccount[];
  /** User's first name */
  firstName: string | null;
  /** User's last name */
  lastName: string | null;
  /** User's username */
  username: string | null;
  /** URL to user's profile image */
  imageUrl: string;
  /** Whether user has profile image */
  hasImage: boolean;
  /** User's primary email address */
  primaryEmailAddress: EmailAddress | null;
  /** User's primary phone number */
  primaryPhoneNumber: PhoneNumber | null;
  /** User's primary web3 wallet */
  primaryWeb3Wallet: Web3Wallet | null;
  /** User's unsafe metadata (client-readable, client-writable) */
  unsafeMetadata: Record<string, any>;
  /** User's public metadata (client-readable, admin-writable) */
  publicMetadata: Record<string, any>;
  /** Timestamp when user was created */
  createdAt: Date;
  /** Timestamp when user was last updated */
  updatedAt: Date;
  /** Update user profile */
  update: (params: UpdateUserParams) => Promise<User>;
  /** Reload user data */
  reload: () => Promise<User>;
  /** Delete user account */
  delete: () => Promise<void>;
  // Additional methods for managing email addresses, phone numbers, etc.
}

Usage Example:

'use client';

import { useUser } from '@clerk/nextjs';

export default function UserProfile() {
  const { isLoaded, isSignedIn, user } = useUser();

  if (!isLoaded || !isSignedIn) {
    return null;
  }

  return (
    <div>
      <h1>{user.firstName} {user.lastName}</h1>
      <p>Email: {user.primaryEmailAddress?.emailAddress}</p>
      <img src={user.imageUrl} alt="Profile" />
    </div>
  );
}

useSession Hook

Returns the current Session object with session data and methods.

/**
 * Returns the current Session object
 * @returns Session object and loading state
 */
function useSession(): UseSessionReturn;

interface UseSessionReturn {
  /** True if session data is loaded */
  isLoaded: boolean;
  /** True if user is signed in */
  isSignedIn: boolean;
  /** Session object, null if signed out, undefined if loading */
  session: Session | null | undefined;
}

interface Session {
  /** Session's unique identifier */
  id: string;
  /** ID of the user this session belongs to */
  userId: string;
  /** Session status: 'active', 'expired', 'abandoned', 'removed' */
  status: SessionStatus;
  /** Timestamp of last activity */
  lastActiveAt: Date;
  /** Timestamp when session expires */
  expireAt: Date;
  /** Timestamp when session is abandoned */
  abandonAt: Date;
  /** Timestamp when session was created */
  createdAt: Date;
  /** Timestamp when session was last updated */
  updatedAt: Date;
  /** Active organization ID */
  lastActiveOrganizationId: string | null;
  /** Active organization slug */
  lastActiveOrganizationSlug: string | null;
  /** Active organization role */
  lastActiveOrganizationRole: string | null;
  /** Actor identifier for impersonation */
  actor: any | null;
  /** Get session token */
  getToken: (options?: GetTokenOptions) => Promise<string | null>;
  /** Check authorization */
  checkAuthorization: (params: CheckAuthorizationParams) => boolean;
  /** Reload session data */
  reload: () => Promise<Session>;
  /** End session */
  end: () => Promise<Session>;
}

Usage Example:

'use client';

import { useSession } from '@clerk/nextjs';

export default function SessionInfo() {
  const { isLoaded, session } = useSession();

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

  return (
    <div>
      <p>Status: {session?.status}</p>
      <p>Last Active: {session?.lastActiveAt.toLocaleString()}</p>
    </div>
  );
}

useClerk Hook

Returns the Clerk instance with methods for managing authentication and navigation.

/**
 * Returns the Clerk instance
 * @returns Clerk instance with authentication methods
 */
function useClerk(): Clerk;

interface Clerk {
  /** Loaded state */
  loaded: boolean;
  /** Current user object */
  user: User | null | undefined;
  /** Current session object */
  session: Session | null | undefined;
  /** Current organization object */
  organization: Organization | null | undefined;
  /** Open sign-in modal or navigate to sign-in page */
  openSignIn: (props?: SignInProps) => void;
  /** Open sign-up modal or navigate to sign-up page */
  openSignUp: (props?: SignUpProps) => void;
  /** Open user profile modal or navigate to user profile page */
  openUserProfile: () => void;
  /** Open organization profile modal or navigate to organization profile page */
  openOrganizationProfile: () => void;
  /** Close current modal */
  closeModal: () => void;
  /** Sign out the current user */
  signOut: (options?: SignOutOptions) => Promise<void>;
  /** Set active session */
  setActive: (params: SetActiveParams) => Promise<void>;
  /** Navigate to a URL */
  navigate: (to: string) => Promise<unknown>;
  /** Build URL with Clerk parameters */
  buildUrl: (to: string, options?: BuildUrlOptions) => string;
}

Usage Example:

'use client';

import { useClerk } from '@clerk/nextjs';

export default function AuthButtons() {
  const clerk = useClerk();

  return (
    <div>
      <button onClick={() => clerk.openSignIn()}>Sign In</button>
      <button onClick={() => clerk.openSignUp()}>Sign Up</button>
      <button onClick={() => clerk.signOut()}>Sign Out</button>
    </div>
  );
}

useOrganization Hook

Returns the current Organization object and methods for managing members and invitations.

/**
 * Returns the current Organization object
 * @returns Organization object, membership, and management methods
 */
function useOrganization(): UseOrganizationReturn;

interface UseOrganizationReturn {
  /** True if organization data is loaded */
  isLoaded: boolean;
  /** Current organization object */
  organization: Organization | null | undefined;
  /** Current user's membership in the organization */
  membership: OrganizationMembership | null | undefined;
  /** List of organization memberships (paginated) */
  memberships: PaginatedResources<OrganizationMembership>;
  /** List of organization invitations (paginated) */
  invitations: PaginatedResources<OrganizationInvitation>;
}

interface Organization {
  /** Organization's unique identifier */
  id: string;
  /** Organization name */
  name: string;
  /** Organization slug */
  slug: string;
  /** URL to organization's logo image */
  imageUrl: string;
  /** Whether organization has logo */
  hasImage: boolean;
  /** Number of members */
  membersCount: number;
  /** Number of pending invitations */
  pendingInvitationsCount: number;
  /** Organization's public metadata */
  publicMetadata: Record<string, any>;
  /** Timestamp when organization was created */
  createdAt: Date;
  /** Timestamp when organization was last updated */
  updatedAt: Date;
  /** Update organization */
  update: (params: UpdateOrganizationParams) => Promise<Organization>;
  /** Delete organization */
  destroy: () => Promise<void>;
  /** Get memberships */
  getMemberships: (params?: GetMembershipsParams) => Promise<OrganizationMembership[]>;
  /** Add member */
  addMember: (params: AddMemberParams) => Promise<OrganizationMembership>;
  /** Invite member */
  inviteMember: (params: InviteMemberParams) => Promise<OrganizationInvitation>;
  /** Update member */
  updateMember: (params: UpdateMemberParams) => Promise<OrganizationMembership>;
  /** Remove member */
  removeMember: (userId: string) => Promise<OrganizationMembership>;
  /** Reload organization data */
  reload: () => Promise<Organization>;
}

Usage Example:

'use client';

import { useOrganization } from '@clerk/nextjs';

export default function OrganizationDashboard() {
  const { isLoaded, organization, memberships } = useOrganization();

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

  if (!organization) {
    return <div>No organization selected</div>;
  }

  return (
    <div>
      <h1>{organization.name}</h1>
      <p>Members: {organization.membersCount}</p>
      <ul>
        {memberships.data?.map((membership) => (
          <li key={membership.id}>{membership.publicUserData.identifier}</li>
        ))}
      </ul>
    </div>
  );
}

useOrganizationList Hook

Returns a list of organizations the user belongs to with methods for creating and switching organizations.

/**
 * Returns list of user's organizations
 * @returns Organization list and management methods
 */
function useOrganizationList(): UseOrganizationListReturn;

interface UseOrganizationListReturn {
  /** True if organization list is loaded */
  isLoaded: boolean;
  /** List of user's organization memberships */
  userMemberships: PaginatedResources<OrganizationMembership>;
  /** List of user's organization invitations */
  userInvitations: PaginatedResources<OrganizationInvitation>;
  /** Set active organization or session */
  setActive: (params: SetActiveParams) => Promise<void>;
  /** Create new organization */
  createOrganization: (params: CreateOrganizationParams) => Promise<Organization>;
}

interface SetActiveParams {
  /** Session ID to set as active */
  session?: string | Session | null;
  /** Organization ID to set as active */
  organization?: string | Organization | null;
  /** Callback before setting active */
  beforeEmit?: (session?: Session | null) => void | Promise<any>;
}

Usage Example:

'use client';

import { useOrganizationList } from '@clerk/nextjs';

export default function OrganizationSelector() {
  const { isLoaded, userMemberships, setActive } = useOrganizationList();

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

  return (
    <select
      onChange={(e) => setActive({ organization: e.target.value })}
    >
      {userMemberships.data?.map((membership) => (
        <option key={membership.id} value={membership.organization.id}>
          {membership.organization.name}
        </option>
      ))}
    </select>
  );
}

useSignIn Hook

Returns sign-in methods and state for building custom sign-in flows.

/**
 * Returns sign-in methods and state
 * @returns SignIn object for managing sign-in flow
 */
function useSignIn(): UseSignInReturn;

interface UseSignInReturn {
  /** True if sign-in data is loaded */
  isLoaded: boolean;
  /** SignIn object */
  signIn: SignIn | undefined;
  /** Set active session after sign-in */
  setActive: (params: SetActiveParams) => Promise<void>;
}

interface SignIn {
  /** Sign-in status */
  status: SignInStatus;
  /** Supported identifiers */
  supportedIdentifiers: string[];
  /** Supported first factors */
  supportedFirstFactors: SignInFactor[];
  /** Supported second factors */
  supportedSecondFactors: SignInFactor[];
  /** Create sign-in with identifier and password */
  create: (params: SignInCreateParams) => Promise<SignIn>;
  /** Verify with strategy */
  prepareFirstFactor: (params: PrepareFirstFactorParams) => Promise<SignIn>;
  /** Attempt first factor verification */
  attemptFirstFactor: (params: AttemptFirstFactorParams) => Promise<SignIn>;
  /** Prepare second factor */
  prepareSecondFactor: (params: PrepareSecondFactorParams) => Promise<SignIn>;
  /** Attempt second factor verification */
  attemptSecondFactor: (params: AttemptSecondFactorParams) => Promise<SignIn>;
  /** Reload sign-in object */
  reload: () => Promise<SignIn>;
}

Usage Example:

'use client';

import { useSignIn } from '@clerk/nextjs';
import { useState } from 'react';

export default function CustomSignIn() {
  const { isLoaded, signIn, setActive } = useSignIn();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!isLoaded) return;

    try {
      const result = await signIn.create({
        identifier: email,
        password,
      });

      if (result.status === 'complete') {
        await setActive({ session: result.createdSessionId });
      }
    } catch (err) {
      console.error('Sign in error:', err);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
      />
      <button type="submit">Sign In</button>
    </form>
  );
}

useSignUp Hook

Returns sign-up methods and state for building custom sign-up flows.

/**
 * Returns sign-up methods and state
 * @returns SignUp object for managing sign-up flow
 */
function useSignUp(): UseSignUpReturn;

interface UseSignUpReturn {
  /** True if sign-up data is loaded */
  isLoaded: boolean;
  /** SignUp object */
  signUp: SignUp | undefined;
  /** Set active session after sign-up */
  setActive: (params: SetActiveParams) => Promise<void>;
}

interface SignUp {
  /** Sign-up status */
  status: SignUpStatus;
  /** Required fields */
  requiredFields: string[];
  /** Optional fields */
  optionalFields: string[];
  /** Missing fields */
  missingFields: string[];
  /** Unverified fields */
  unverifiedFields: string[];
  /** Verifications */
  verifications: SignUpVerifications;
  /** Username */
  username: string | null;
  /** Email address */
  emailAddress: string | null;
  /** Phone number */
  phoneNumber: string | null;
  /** First name */
  firstName: string | null;
  /** Last name */
  lastName: string | null;
  /** Unsafe metadata */
  unsafeMetadata: Record<string, any>;
  /** Created session ID */
  createdSessionId: string | null;
  /** Created user ID */
  createdUserId: string | null;
  /** Create sign-up */
  create: (params: SignUpCreateParams) => Promise<SignUp>;
  /** Update sign-up */
  update: (params: SignUpUpdateParams) => Promise<SignUp>;
  /** Prepare verification */
  prepareVerification: (params: PrepareVerificationParams) => Promise<SignUp>;
  /** Attempt verification */
  attemptVerification: (params: AttemptVerificationParams) => Promise<SignUp>;
  /** Reload sign-up object */
  reload: () => Promise<SignUp>;
}

Usage Example:

'use client';

import { useSignUp } from '@clerk/nextjs';
import { useState } from 'react';

export default function CustomSignUp() {
  const { isLoaded, signUp, setActive } = useSignUp();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [code, setCode] = useState('');
  const [verifying, setVerifying] = useState(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!isLoaded) return;

    try {
      await signUp.create({
        emailAddress: email,
        password,
      });

      await signUp.prepareVerification({ strategy: 'email_code' });
      setVerifying(true);
    } catch (err) {
      console.error('Sign up error:', err);
    }
  };

  const handleVerify = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!isLoaded) return;

    try {
      const result = await signUp.attemptVerification({ code });

      if (result.status === 'complete') {
        await setActive({ session: result.createdSessionId });
      }
    } catch (err) {
      console.error('Verification error:', err);
    }
  };

  if (verifying) {
    return (
      <form onSubmit={handleVerify}>
        <input
          type="text"
          value={code}
          onChange={(e) => setCode(e.target.value)}
          placeholder="Verification code"
        />
        <button type="submit">Verify</button>
      </form>
    );
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
      />
      <button type="submit">Sign Up</button>
    </form>
  );
}

useSessionList Hook

Returns a list of the user's active sessions with methods for switching sessions.

/**
 * Returns list of user's sessions
 * @returns Session list and management methods
 */
function useSessionList(): UseSessionListReturn;

interface UseSessionListReturn {
  /** True if session list is loaded */
  isLoaded: boolean;
  /** List of user's sessions */
  sessions: Session[] | undefined;
  /** Set active session */
  setActive: (params: SetActiveParams) => Promise<void>;
}

Usage Example:

'use client';

import { useSessionList } from '@clerk/nextjs';

export default function SessionManager() {
  const { isLoaded, sessions, setActive } = useSessionList();

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

  return (
    <div>
      <h2>Active Sessions</h2>
      {sessions?.map((session) => (
        <div key={session.id}>
          <p>Last active: {session.lastActiveAt.toLocaleString()}</p>
          <button onClick={() => setActive({ session: session.id })}>
            Switch to this session
          </button>
        </div>
      ))}
    </div>
  );
}

useEmailLink Hook

Returns utilities for email link verification flows.

/**
 * Returns email link verification utilities
 * @returns Email link methods
 */
function useEmailLink(): UseEmailLinkReturn;

interface UseEmailLinkReturn {
  /** Start email link flow */
  startEmailLinkFlow: (params: StartEmailLinkFlowParams) => Promise<void>;
  /** Cancel email link flow */
  cancelEmailLinkFlow: () => void;
}

useReverification Hook

Returns reverification utilities for sensitive operations.

/**
 * Returns reverification utilities
 * @returns Reverification methods and state
 */
function useReverification(): UseReverificationReturn;

interface UseReverificationReturn {
  /** Reverify user for sensitive operation */
  reverify: () => Promise<void>;
  /** Reverification status */
  status: 'idle' | 'loading' | 'success' | 'error';
}

Common Types

interface PaginatedResources<T> {
  data: T[] | undefined;
  count: number | undefined;
  isLoading: boolean;
  isFetching: boolean;
  isError: boolean;
  page: number;
  pageCount: number | undefined;
  fetchNext: () => Promise<void>;
  fetchPrevious: () => Promise<void>;
  hasNextPage: boolean;
  hasPreviousPage: boolean;
  revalidate: () => Promise<void>;
  setPage: (page: number) => void;
}