Setting the file. One moment.
Subchapter 5.3
core-3/custom-sign-in.mdMarkdown9 KBView on GitHub
import { useSignIn } from '@clerk/nextjs' // or @clerk/react, @clerk/expo
const { signIn, errors, fetchStatus } = useSignIn()| Property | Type | Description |
|---|---|---|
signIn | SignInFuture | Sign-in object with namespaced methods |
errors | Errors<SignInFields> | Structured error object |
fetchStatus | 'idle' | 'fetching' | Network request status |
const { error } = await signIn.password({
identifier: 'user@example.com',
password: 'securePassword123',
})const { error } = await signIn.sso({
strategy: 'oauth_google', // or 'oauth_github', 'enterprise_sso', etc.
redirectUrl: '/dashboard', // where to go after SSO completes
redirectCallbackUrl: '/sso-callback', // intermediate callback route
})const { error } = await signIn.passkey({ flow: 'discoverable' })const { error } = await signIn.web3({ strategy: 'web3_solana_signature' })
// or
const { error } = await signIn.web3({ strategy: 'web3_base_signature' })const { error } = await signIn.ticket({ ticket: 'ticket_abc123' })// Send code (emailAddress is optional if a signIn already exists from a prior method call)
const { error } = await signIn.emailCode.sendCode({ emailAddress: 'user@example.com' })
// Verify code
const { error } = await signIn.emailCode.verifyCode({ code: '123456' })// Send code (phoneNumber is optional if a signIn already exists from a prior method call)
const { error } = await signIn.phoneCode.sendCode({ phoneNumber: '+12015551234' })
// Verify code
const { error } = await signIn.phoneCode.verifyCode({ code: '123456' })A second factor is required when signIn.status is one of:
'needs_second_factor' — user has MFA enabled (TOTP, backup codes, etc.)'needs_client_trust' — new device sign-in without MFA; requires email or phone code verification// TOTP (Authenticator app)
const { error } = await signIn.mfa.verifyTOTP({ code: '123456' })
// Backup code
const { error } = await signIn.mfa.verifyBackupCode({ code: 'backup-code-here' })
// Email code
const { error: sendErr } = await signIn.mfa.sendEmailCode()
const { error: verifyErr } = await signIn.mfa.verifyEmailCode({ code: '123456' })
// Phone code
const { error: sendErr } = await signIn.mfa.sendPhoneCode()
const { error: verifyErr } = await signIn.mfa.verifyPhoneCode({ code: '123456' })// 1. Send reset code
const { error } = await signIn.resetPasswordEmailCode.sendCode()
// 2. Verify the code
const { error } = await signIn.resetPasswordEmailCode.verifyCode({ code: '123456' })
// 3. Submit new password
const { error } = await signIn.resetPasswordEmailCode.submitPassword({
password: 'newSecurePassword123',
})When a user signs in with a valid password from a new device without MFA enabled, the sign-in status becomes needs_client_trust. This requires an additional verification step:
if (signIn.status === 'needs_client_trust') {
// Check supportedSecondFactors for available methods (email_code or phone_code)
const factors = signIn.supportedSecondFactors
// Use the appropriate mfa method to verify
}After successful authentication, call finalize() to activate the session:
await signIn.finalize({
navigate: async ({ session, decorateUrl }) => {
const destination = session.currentTask
? `/sign-in/tasks/${session.currentTask.key}`
: '/'
const url = decorateUrl(destination)
// decorateUrl may return an absolute URL for Safari ITP
if (url.startsWith('http')) {
window.location.href = url
} else {
router.push(url)
}
},
})decorateUrl(path) — decorates the URL with session info (required to support Safari’s Intelligent Tracking Prevention). May return an absolute URL.session.currentTask — check for pending session tasks before redirectingClear local sign-in state and start over:
signIn.reset()All methods return Promise<{ error: ClerkError | null }>. Errors are also available reactively on the hook:
const { signIn, errors } = useSignIn()
// Field-level errors
errors?.fields?.identifier // { code, message, longMessage? }
errors?.fields?.password // { code, message, longMessage? }
errors?.fields?.code // { code, message, longMessage? }
// Global errors (not tied to a field)
errors?.global // ClerkGlobalHookError[] | null
// Raw error array
errors?.raw // ClerkError[] | nullFrom the docs (opens in a new tab). Supports SMS verification codes, authenticator app (TOTP), and backup codes.
'use client'
import { useSignIn } from '@clerk/nextjs'
import { useRouter } from 'next/navigation'
export default function Page() {
const { signIn, errors, fetchStatus } = useSignIn()
const router = useRouter()
const handleSubmit = async (formData: