šŸ“ Sign Up | šŸ” Log In

← Root | ↑ Up

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” │ šŸ“„ shadcn/directory/clerk/clerk-docs/reference/hooks/use-reverification │ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

╔══════════════════════════════════════════════════════════════════════════════════════════════╗
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘

title: useReverification() description: Clerk's useReverification() hook enhances a fetcher function to handle a session's reverification flow. sdk: chrome-extension, expo, nextjs, react, react-router, remix, tanstack-react-start

<Include src="_partials/reverification-callout" />

Reverification allows you to prompt a user to verify their credentials before performing sensitive actions, even if they're already authenticated. For example, in a banking application, transferring money is considered a "sensitive action." Reverification can be used to confirm the user's identity.

The useReverification() hook is used to handle a session's reverification flow. If a request requires reverification, a modal will display, prompting the user to verify their credentials. Upon successful verification, the original request will automatically retry. If you'd like to build a custom UI, you can use the onNeedsReverification option.

When using reverification, a user's credentials are valid for 10 minutes. Once stale, a user will need to reverify their credentials. This time duration can be customized by using the has() helper on the server-side. See the guide on reverification for more information.

Examples

The useReverification() hook displays a prebuilt UI when the user needs to reverify their credentials. You can also build a custom UI to handle the reverification process yourself. Use the following tabs to see examples of either option.

<Tabs items={["Prebuilt UI", "Custom UI"]}> <Tab> <If sdk="expo"> > [!WARNING] > Prebuilt UI is only supported for Expo web apps. For mobile apps, you need to build a custom UI. </If>

### Handle reverification for an action

The following example demonstrates how to use the `useReverification()` hook to require a user to reverify their credentials before being able to update their primary email address. It also demonstrates how to handle the cancellation of the reverification process.

<If sdk="react">
  ```tsx {{ filename: 'src/components/UpdateUserEmail.tsx', collapsible: true }}
  import { useReverification, useUser } from '@clerk/clerk-react'
  import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/clerk-react/errors'

  export function UpdateUserEmail() {
    // Use `useUser()` to get the current user's `User` object
    // `User` includes the `update()` method to update the user's primary email address
    const { user } = useUser()

    // Use `useReverification()` to enhance the `update()` method
    // to handle the reverification process
    const changePrimaryEmail = useReverification((emailAddressId: string) =>
      user?.update({ primaryEmailAddressId: emailAddressId }),
    )

    const handleClick = async (emailAddressId: string) => {
      try {
        await changePrimaryEmail(emailAddressId)
      } catch (e) {
        // Handle if user cancels the reverification process
        if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
          console.error('User cancelled reverification', e.code)
        }

        // Handle other errors
        // See https://clerk.com/docs/custom-flows/error-handling
        // for more info on error handling
        console.error(JSON.stringify(e, null, 2))
      }
    }

    return (
      <div>
        <span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>

        <ul>
          {user?.emailAddresses.map((email) => (
            <li key={email.id}>
              <span>{email.emailAddress}</span>
              {email.id !== user?.primaryEmailAddress?.id && (
                <button onClick={() => handleClick(email.id)}>Make primary</button>
              )}
            </li>
          ))}
        </ul>
      </div>
    )
  }
  ```
</If>

<If sdk="nextjs">
  ```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
  'use client'

  import { useReverification, useUser } from '@clerk/nextjs'
  import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/nextjs/errors'

  export function UpdateUserEmail() {
    // Use `useUser()` to get the current user's `User` object
    // `User` includes the `update()` method to update the user's primary email address
    const { user } = useUser()

    // Use `useReverification()` to enhance the `update()` method
    // to handle the reverification process
    const changePrimaryEmail = useReverification((emailAddressId: string) =>
      user?.update({ primaryEmailAddressId: emailAddressId }),
    )

    const handleClick = async (emailAddressId: string) => {
      try {
        await changePrimaryEmail(emailAddressId)
      } catch (e) {
        // Handle if user cancels the reverification process
        if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
          console.error('User cancelled reverification', e.code)
        }

        // Handle other errors
        // See https://clerk.com/docs/custom-flows/error-handling
        // for more info on error handling
        console.error(JSON.stringify(e, null, 2))
      }
    }

    return (
      <div>
        <span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>

        <ul>
          {user?.emailAddresses.map((email) => (
            <li key={email.id}>
              <span>{email.emailAddress}</span>
              {email.id !== user?.primaryEmailAddress?.id && (
                <button onClick={() => handleClick(email.id)}>Make primary</button>
              )}
            </li>
          ))}
        </ul>
      </div>
    )
  }
  ```
</If>

<If sdk="react-router">
  ```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
  import { useReverification, useUser } from '@clerk/react-router'
  import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'

  export function UpdateUserEmail() {
    // Use `useUser()` to get the current user's `User` object
    // `User` includes the `update()` method to update the user's primary email address
    const { user } = useUser()

    // Use `useReverification()` to enhance the `update()` method
    // to handle the reverification process
    const changePrimaryEmail = useReverification((emailAddressId: string) =>
      user?.update({ primaryEmailAddressId: emailAddressId }),
    )

    const handleClick = async (emailAddressId: string) => {
      try {
        await changePrimaryEmail(emailAddressId)
      } catch (e) {
        // Handle if user cancels the reverification process
        if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
          console.error('User cancelled reverification', e.code)
        }

        // Handle other errors
        // See https://clerk.com/docs/custom-flows/error-handling
        // for more info on error handling
        console.error(JSON.stringify(e, null, 2))
      }
    }

    return (
      <div>
        <span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>

        <ul>
          {user?.emailAddresses.map((email) => (
            <li key={email.id}>
              <span>{email.emailAddress}</span>
              {email.id !== user?.primaryEmailAddress?.id && (
                <button onClick={() => handleClick(email.id)}>Make primary</button>
              )}
            </li>
          ))}
        </ul>
      </div>
    )
  }
  ```
</If>

<If sdk="chrome-extension">
  ```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
  import { useReverification, useUser } from '@clerk/chrome-extension'
  import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'

  export function UpdateUserEmail() {
    // Use `useUser()` to get the current user's `User` object
    // `User` includes the `update()` method to update the user's primary email address
    const { user } = useUser()

    // Use `useReverification()` to enhance the `update()` method
    // to handle the reverification process
    const changePrimaryEmail = useReverification((emailAddressId: string) =>
      user?.update({ primaryEmailAddressId: emailAddressId }),
    )

    const handleClick = async (emailAddressId: string) => {
      try {
        await changePrimaryEmail(emailAddressId)
      } catch (e) {
        // Handle if user cancels the reverification process
        if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
          console.error('User cancelled reverification', e.code)
        }

        // Handle other errors
        // See https://clerk.com/docs/custom-flows/error-handling
        // for more info on error handling
        console.error(JSON.stringify(e, null, 2))
      }
    }

    return (
      <div>
        <span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>

        <ul>
          {user?.emailAddresses.map((email) => (
            <li key={email.id}>
              <span>{email.emailAddress}</span>
              {email.id !== user?.primaryEmailAddress?.id && (
                <button onClick={() => handleClick(email.id)}>Make primary</button>
              )}
            </li>
          ))}
        </ul>
      </div>
    )
  }
  ```
</If>

<If sdk="remix">
  ```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
  import { useReverification, useUser } from '@clerk/remix'
  import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'

  export function UpdateUserEmail() {
    // Use `useUser()` to get the current user's `User` object
    // `User` includes the `update()` method to update the user's primary email address
    const { user } = useUser()

    // Use `useReverification()` to enhance the `update()` method
    // to handle the reverification process
    const changePrimaryEmail = useReverification((emailAddressId: string) =>
      user?.update({ primaryEmailAddressId: emailAddressId }),
    )

    const handleClick = async (emailAddressId: string) => {
      try {
        await changePrimaryEmail(emailAddressId)
      } catch (e) {
        // Handle if user cancels the reverification process
        if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
          console.error('User cancelled reverification', e.code)
        }

        // Handle other errors
        // See https://clerk.com/docs/custom-flows/error-handling
        // for more info on error handling
        console.error(JSON.stringify(e, null, 2))
      }
    }

    return (
      <div>
        <span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>

        <ul>
          {user?.emailAddresses.map((email) => (
            <li key={email.id}>
              <span>{email.emailAddress}</span>
              {email.id !== user?.primaryEmailAddress?.id && (
                <button onClick={() => handleClick(email.id)}>Make primary</button>
              )}
            </li>
          ))}
        </ul>
      </div>
    )
  }
  ```
</If>

<If sdk="tanstack-react-start">
  ```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
  import { useReverification, useUser } from '@clerk/tanstack-react-start'
  import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'

  export function UpdateUserEmail() {
    // Use `useUser()` to get the current user's `User` object
    // `User` includes the `update()` method to update the user's primary email address
    const { user } = useUser()

    // Use `useReverification()` to enhance the `update()` method
    // to handle the reverification process
    const changePrimaryEmail = useReverification((emailAddressId: string) =>
      user?.update({ primaryEmailAddressId: emailAddressId }),
    )

    const handleClick = async (emailAddressId: string) => {
      try {
        await changePrimaryEmail(emailAddressId)
      } catch (e) {
        // Handle if user cancels the reverification process
        if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
          console.error('User cancelled reverification', e.code)
        }

        // Handle other errors
        // See https://clerk.com/docs/custom-flows/error-handling
        // for more info on error handling
        console.error(JSON.stringify(e, null, 2))
      }
    }

    return (
      <div>
        <span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>

        <ul>
          {user?.emailAddresses.map((email) => (
            <li key={email.id}>
              <span>{email.emailAddress}</span>
              {email.id !== user?.primaryEmailAddress?.id && (
                <button onClick={() => handleClick(email.id)}>Make primary</button>
              )}
            </li>
          ))}
        </ul>
      </div>
    )
  }
  ```
</If>

<If sdk="expo">
  ```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
  import { useReverification, useUser } from '@clerk/clerk-expo'
  import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
  import { Text, View, TouchableOpacity, ScrollView } from 'react-native'

  export function UpdateUserEmail() {
    // Use `useUser()` to get the current user's `User` object
    // `User` includes the `update()` method to update the user's primary email address
    const { user } = useUser()

    // Use `useReverification()` to enhance the `update()` method
    // to handle the reverification process
    const changePrimaryEmail = useReverification((emailAddressId: string) =>
      user?.update({ primaryEmailAddressId: emailAddressId }),
    )

    const handleClick = async (emailAddressId: string) => {
      try {
        await changePrimaryEmail(emailAddressId)
      } catch (e) {
        // Handle if user cancels the reverification process
        if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
          console.error('User cancelled reverification', e.code)
        }

        // Handle other errors
        // See https://clerk.com/docs/custom-flows/error-handling
        // for more info on error handling
        console.error(JSON.stringify(e, null, 2))
      }
    }

    return (
      <View>
        <Text>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</Text>

        <ScrollView>
          {user?.emailAddresses.map((email) => (
            <View key={user.id}>
              <Text>{email.emailAddress}</Text>
              {email.id !== user?.primaryEmailAddress?.id && (
                <TouchableOpacity onPress={() => handleClick(email.id)}>
                  <Text>Make primary</Text>
                </TouchableOpacity>
              )}
            </View>
          ))}
        </ScrollView>
      </View>
    )
  }
  ```
</If>

### Handle reverification for a fetcher function

The following example demonstrates how to use the `useReverification()` hook to enhance a fetcher function that fetches data from a route that requires reverification. For examples on how to set up a route that requires reverification, see the [guide on reverification](/docs/guides/secure/reverification).

<If sdk="react">
  ```tsx {{ filename: 'src/components/AccountBalance.tsx' }}
  import { useAuth, useReverification } from '@clerk/clerk-react'
  import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/clerk-react/errors'
  import { useState } from 'react'

  export function AccountBalance() {
    const { getToken } = useAuth()
    const [balance, setBalance] = useState<number | null>(null)
    const accountBalance = useReverification(async () => {
      const response = await fetch('/api/balance', {
        headers: {
          Authorization: `Bearer ${await getToken()}`,
        },
      })
      return await response.json()
    })

    const handleClick = async () => {
      try {
        const accountBalanceResponse = await accountBalance()

        setBalance(accountBalanceResponse.amount)
      } catch (e) {
        // Handle if user cancels the reverification process
        if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
          console.error('User cancelled reverification', e.code)
        }

        // Handle other errors
        // See https://clerk.com/docs/custom-flows/error-handling
        // for more info on error handling
        console.error('Error fetching account balance', e)
      }
    }

    return (
      <div>
        <span>Your account balance is {balance ? `$${balance}` : '$******'}</span>
        <button onClick={() => handleClick()}>See account balance</button>
      </div>
    )
  }
  ```
</If>

<If sdk="nextjs">
  ```tsx {{ filename: 'components/AccountBalance.tsx' }}
  'use client'

  import { useReverification } from '@clerk/nextjs'
  import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/nextjs/errors'
  import { useState } from 'react'

  export function AccountBalance() {
    const [balance, setBalance] = useState<number | null>(null)
    const accountBalance = useReverification(async () => {
      const response = await fetch('/api/balance')
      return await response.json()
    })

    const handleClick = async () => {
      try {
        const accountBalanceResponse = await accountBalance()

        setBalance(accountBalanceResponse.amount)
      } catch (e) {
        // Handle if user cancels the reverification process
        if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
          console.error('User cancelled reverification', e.code)
        }

        // Handle other errors
        // See https://clerk.com/docs/custom-flows/error-handling
        // for more info on error handling
        console.error(JSON.stringify(e, null, 2))
      }
    }

    return (
      <div>
        <span>Your account balance is {balance ? `$${balance}` : '$******'}</span>
        <button onClick={() => handleClick()}>See account balance</button>
      </div>
    )
  }
  ```
</If>

<If sdk="react-router">
  ```tsx {{ filename: 'components/AccountBalance.tsx' }}
  import { useReverification } from '@clerk/react-router'
  import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
  import { useState } from 'react'

  export function AccountBalance() {
    const [balance, setBalance] = useState<number | null>(null)
    const accountBalance = useReverification(async () => {
      const response = await fetch('/api/balance')
      return await response.json()
    })

    const handleClick = async () => {
      try {
        const accountBalanceResponse = await accountBalance()

        setBalance(accountBalanceResponse.amount)
      } catch (e) {
        // Handle if user cancels the reverification process
        if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
          console.error('User cancelled reverification', e.code)
        }

        // Handle other errors
        // See https://clerk.com/docs/custom-flows/error-handling
        // for more info on error handling
        console.error(JSON.stringify(e, null, 2))
      }
    }

    return (
      <div>
        <span>Your account balance is {balance ? `$${balance}` : '$******'}</span>
        <button onClick={() => handleClick()}>See account balance</button>
      </div>
    )
  }
  ```
</If>

<If sdk="chrome-extension">
  ```tsx {{ filename: 'components/AccountBalance.tsx' }}
  import { useAuth, useReverification } from '@clerk/chrome-extension'
  import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
  import { useState } from 'react'

  export function AccountBalance() {
    const { getToken } = useAuth()
    const [balance, setBalance] = useState<number | null>(null)
    const accountBalance = useReverification(async () => {
      const response = await fetch('/api/balance', {
        headers: {
          Authorization: `Bearer ${await getToken()}`,
        },
      })
      return await response.json()
    })

    const handleClick = async () => {
      try {
        const accountBalanceResponse = await accountBalance()

        setBalance(accountBalanceResponse.amount)
      } catch (e) {
        // Handle if user cancels the reverification process
        if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
          console.error('User cancelled reverification', e.code)
        }

        // Handle other errors
        // See https://clerk.com/docs/custom-flows/error-handling
        // for more info on error handling
        console.error('Error fetching account balance', e)
      }
    }

    return (
      <div>
        <span>Your account balance is {balance ? `$${balance}` : '$******'}</span>
        <button onClick={() => handleClick()}>See account balance</button>
      </div>
    )
  }
  ```
</If>

<If sdk="remix">
  ```tsx {{ filename: 'components/AccountBalance.tsx' }}
  import { useReverification } from '@clerk/remix'
  import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
  import { useState } from 'react'

  export function AccountBalance() {
    const [balance, setBalance] = useState<number | null>(null)
    const accountBalance = useReverification(async () => {
      const response = await fetch('/api/balance')
      return await response.json()
    })

    const handleClick = async () => {
      try {
        const accountBalanceResponse = await accountBalance()

        setBalance(accountBalanceResponse.amount)
      } catch (e) {
        // Handle if user cancels the reverification process
        if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
          console.error('User cancelled reverification', e.code)
        }

        // Handle other errors
        // See https://clerk.com/docs/custom-flows/error-handling
        // for more info on error handling
        console.error(JSON.stringify(e, null, 2))
      }
    }

    return (
      <div>
        <span>Your account balance is {balance ? `$${balance}` : '$******'}</span>
        <button onClick={() => handleClick()}>See account balance</button>
      </div>
    )
  }
  ```
</If>

<If sdk="tanstack-react-start">
  ```tsx {{ filename: 'src/components/AccountBalance.tsx' }}
  import { useReverification } from '@clerk/tanstack-react-start'
  import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
  import { useState } from 'react'

  export function AccountBalance() {
    const [balance, setBalance] = useState<number | null>(null)
    const accountBalance = useReverification(async () => {
      const response = await fetch('/api/balance')
      return await response.json()
    })

    const handleClick = async () => {
      try {
        const accountBalanceResponse = await accountBalance()

        setBalance(accountBalanceResponse.amount)
      } catch (e) {
        // Handle if user cancels the reverification process
        if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
          console.error('User cancelled reverification', e.code)
        }

        // Handle other errors
        // See https://clerk.com/docs/custom-flows/error-handling
        // for more info on error handling
        console.error(JSON.stringify(e, null, 2))
      }
    }

    return (
      <div>
        <span>Your account balance is {balance ? `$${balance}` : '$******'}</span>
        <button onClick={() => handleClick()}>See account balance</button>
      </div>
    )
  }
  ```
</If>

<If sdk="expo">
  ```tsx {{ filename: 'components/AccountBalance.tsx' }}
  import { useAuth, useReverification } from '@clerk/clerk-expo'
  import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
  import { useState } from 'react'
  import { Text, View, TouchableOpacity } from 'react-native'

  export function AccountBalance() {
    const { getToken } = useAuth()

    const [balance, setBalance] = useState<number | null>(null)
    const accountBalance = useReverification(async () => {
      const response = await fetch('/api/balance', {
        headers: {
          Authorization: `Bearer ${await getToken()}`,
        },
      })
      return await response.json()
    })

    const handleClick = async () => {
      try {
        const accountBalanceResponse = await accountBalance()

        setBalance(accountBalanceResponse.amount)
      } catch (e) {
        // Handle if user cancels the reverification process
        if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
          console.error('User cancelled reverification', e.code)
        }

        // Handle other errors
        // See https://clerk.com/docs/custom-flows/error-handling
        // for more info on error handling
        console.error('Error fetching account balance', e)
      }
    }

    return (
      <View>
        <Text>Your account balance is {balance ? `$${balance}` : '$******'}</Text>
        <TouchableOpacity onPress={() => handleClick()}>
          <Text>See account balance</Text>
        </TouchableOpacity>
      </View>
    )
  }
  ```
</If>
</Tab> <Tab> The following example demonstrates how to build a custom UI when using the `useReverification()` hook. In the example, the `useReverification()` hook is used to require a user to reverify their credentials before being able to update their primary email address. It requires two components: the `<UpdateUserEmail />` component displays the list of email addresses to choose from and it renders the second component, `<VerificationComponent />`, which handles the reverification process.
The example handles first factor verification using an email code, so you will need to have the [**Email verification code**](/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) setting enabled for your application. But you can adapt this example to handle any type of verification level or strategy.

<Tabs items={["UpdateUserEmail", "VerificationComponent"]}>
  <Tab>
    The `<UpdateUserEmail />` component uses `useReverification()` to enhance the `update()` method, requiring the user to reverify their credentials before being able to update their primary email address.

    The `useReverification()` hook provides the `onNeedsReverification` option, which is a handler for building a custom UI. It provides four properties: `level`, `complete`, `cancel`, and `inProgress`. The example tracks these using the `verificationState` state variable.

    - The `level` property determines the verification level required for the reverification process. This example only handles first factor verification, which is done in the `<VerificationComponent>` component.
    - The `complete` and `cancel` properties are the steps of the reverification process, which is also done in the `<VerificationComponent>` component.
    - The `inProgress` property is used to track the state of the reverification process. When the user selects the "Make primary" button, it triggers the reverification process and sets the `inProgress` property is `true`, which displays the `<VerificationComponent>` component.

    <If sdk="react">
      ```tsx {{ filename: 'src/components/UpdateUserEmail.tsx', collapsible: true }}
      import { useReverification, useUser } from '@clerk/clerk-react'
      import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/clerk-react/errors'
      import { useState } from 'react'
      import { SessionVerificationLevel } from '@clerk/types'
      import { VerificationComponent } from './VerificationComponent'

      export function UpdateUserEmail() {
        // Use `useUser()` to get the current user's `User` object
        // `User` includes the `update()` method to update the user's primary email address
        const { user } = useUser()

        // TODO: Update to use exported type once available
        const [verificationState, setVerificationState] = useState<
          | {
              complete: () => void
              cancel: () => void
              level: SessionVerificationLevel | undefined
              inProgress: boolean
            }
          | undefined
        >(undefined)

        // Use `useReverification()` to enhance the `update()` method
        // to handle the reverification process
        const changePrimaryEmail = useReverification(
          (emailAddressId: string) => user?.update({ primaryEmailAddressId: emailAddressId }),
          {
            onNeedsReverification: ({ complete, cancel, level }) => {
              setVerificationState({
                complete,
                cancel,
                level,
                inProgress: true,
              })
            },
          },
        )

        const handleClick = async (emailAddressId: string) => {
          try {
            await changePrimaryEmail(emailAddressId)
          } catch (e) {
            // Handle if user cancels the reverification process
            if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
              console.error('User cancelled reverification', e.code)
            }

            // Handle other errors
            // See https://clerk.com/docs/custom-flows/error-handling
            // for more info on error handling
            console.error(JSON.stringify(e, null, 2))
          }
        }

        return (
          <div>
            <span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>

            <ul>
              {user?.emailAddresses.map((email) => (
                <li key={email.id}>
                  <span>{email.emailAddress}</span>
                  {user?.primaryEmailAddressId !== email.id && (
                    <button onClick={() => handleClick(email.id)}>Make primary</button>
                  )}
                </li>
              ))}
            </ul>

            {verificationState?.inProgress && (
              <VerificationComponent
                level={verificationState?.level}
                onComplete={() => {
                  verificationState.complete()
                  setVerificationState(undefined)
                }}
                onCancel={() => {
                  verificationState.cancel()
                  setVerificationState(undefined)
                }}
              />
            )}
          </div>
        )
      }
      ```
    </If>

    <If sdk="nextjs">
      ```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
      'use client'

      import { useReverification, useUser } from '@clerk/nextjs'
      import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/nextjs/errors'
      import { useState } from 'react'
      import { SessionVerificationLevel } from '@clerk/types'
      import { VerificationComponent } from './VerificationComponent'

      export function UpdateUserEmail() {
        // Use `useUser()` to get the current user's `User` object
        // `User` includes the `update()` method to update the user's primary email address
        const { user } = useUser()

        // TODO: Update to use exported type once available
        const [verificationState, setVerificationState] = useState<
          | {
              complete: () => void
              cancel: () => void
              level: SessionVerificationLevel | undefined
              inProgress: boolean
            }
          | undefined
        >(undefined)

        // Use `useReverification()` to enhance the `update()` method
        // to handle the reverification process
        const changePrimaryEmail = useReverification(
          (emailAddressId: string) => user?.update({ primaryEmailAddressId: emailAddressId }),
          {
            onNeedsReverification: ({ complete, cancel, level }) => {
              setVerificationState({
                complete,
                cancel,
                level,
                inProgress: true,
              })
            },
          },
        )

        const handleClick = async (emailAddressId: string) => {
          try {
            await changePrimaryEmail(emailAddressId)
          } catch (e) {
            // Handle if user cancels the reverification process
            if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
              console.error('User cancelled reverification', e.code)
            }

            // Handle other errors
            // See https://clerk.com/docs/custom-flows/error-handling
            // for more info on error handling
            console.error(JSON.stringify(e, null, 2))
          }
        }

        return (
          <div>
            <span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>

            <ul>
              {user?.emailAddresses.map((email) => (
                <li key={email.id}>
                  <span>{email.emailAddress}</span>
                  {user?.primaryEmailAddressId !== email.id && (
                    <button onClick={() => handleClick(email.id)}>Make primary</button>
                  )}
                </li>
              ))}
            </ul>

            {verificationState?.inProgress && (
              <VerificationComponent
                level={verificationState?.level}
                onComplete={() => {
                  verificationState.complete()
                  setVerificationState(undefined)
                }}
                onCancel={() => {
                  verificationState.cancel()
                  setVerificationState(undefined)
                }}
              />
            )}
          </div>
        )
      }
      ```
    </If>

    <If sdk="react-router">
      ```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
      import { useReverification, useUser } from '@clerk/react-router'
      import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
      import { useState } from 'react'
      import type { SessionVerificationLevel } from '@clerk/types'
      import { VerificationComponent } from './VerificationComponent'

      export function UpdateUserEmail() {
        // Use `useUser()` to get the current user's `User` object
        // `User` includes the `update()` method to update the user's primary email address
        const { user } = useUser()

        // TODO: Update to use exported type once available
        const [verificationState, setVerificationState] = useState<
          | {
              complete: () => void
              cancel: () => void
              level: SessionVerificationLevel | undefined
              inProgress: boolean
            }
          | undefined
        >(undefined)

        // Use `useReverification()` to enhance the `update()` method
        // to handle the reverification process
        const changePrimaryEmail = useReverification(
          (emailAddressId: string) => user?.update({ primaryEmailAddressId: emailAddressId }),
          {
            onNeedsReverification: ({ complete, cancel, level }) => {
              setVerificationState({
                complete,
                cancel,
                level,
                inProgress: true,
              })
            },
          },
        )

        const handleClick = async (emailAddressId: string) => {
          try {
            await changePrimaryEmail(emailAddressId)
          } catch (e) {
            // Handle if user cancels the reverification process
            if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
              console.error('User cancelled reverification', e.code)
            }

            // Handle other errors
            // See https://clerk.com/docs/custom-flows/error-handling
            // for more info on error handling
            console.error(JSON.stringify(e, null, 2))
          }
        }

        return (
          <div>
            <span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>

            <ul>
              {user?.emailAddresses.map((email) => (
                <li key={email.id}>
                  <span>{email.emailAddress}</span>
                  {user?.primaryEmailAddressId !== email.id && (
                    <button onClick={() => handleClick(email.id)}>Make primary</button>
                  )}
                </li>
              ))}
            </ul>

            {verificationState?.inProgress && (
              <VerificationComponent
                level={verificationState?.level}
                onComplete={() => {
                  verificationState.complete()
                  setVerificationState(undefined)
                }}
                onCancel={() => {
                  verificationState.cancel()
                  setVerificationState(undefined)
                }}
              />
            )}
          </div>
        )
      }
      ```
    </If>

    <If sdk="chrome-extension">
      ```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
      import { useReverification, useUser } from '@clerk/chrome-extension'
      import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
      import { useState } from 'react'
      import type { SessionVerificationLevel } from '@clerk/types'
      import { VerificationComponent } from './VerificationComponent'

      export function UpdateUserEmail() {
        // Use `useUser()` to get the current user's `User` object
        // `User` includes the `update()` method to update the user's primary email address
        const { user } = useUser()

        // TODO: Update to use exported type once available
        const [verificationState, setVerificationState] = useState<
          | {
              complete: () => void
              cancel: () => void
              level: SessionVerificationLevel | undefined
              inProgress: boolean
            }
          | undefined
        >(undefined)

        // Use `useReverification()` to enhance the `update()` method
        // to handle the reverification process
        const changePrimaryEmail = useReverification(
          (emailAddressId: string) => user?.update({ primaryEmailAddressId: emailAddressId }),
          {
            onNeedsReverification: ({ complete, cancel, level }) => {
              setVerificationState({
                complete,
                cancel,
                level,
                inProgress: true,
              })
            },
          },
        )

        const handleClick = async (emailAddressId: string) => {
          try {
            await changePrimaryEmail(emailAddressId)
          } catch (e) {
            // Handle if user cancels the reverification process
            if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
              console.error('User cancelled reverification', e.code)
            }

            // Handle other errors
            // See https://clerk.com/docs/custom-flows/error-handling
            // for more info on error handling
            console.error(JSON.stringify(e, null, 2))
          }
        }

        return (
          <div>
            <span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>

            <ul>
              {user?.emailAddresses.map((email) => (
                <li key={email.id}>
                  <span>{email.emailAddress}</span>
                  {user?.primaryEmailAddressId !== email.id && (
                    <button onClick={() => handleClick(email.id)}>Make primary</button>
                  )}
                </li>
              ))}
            </ul>

            {verificationState?.inProgress && (
              <VerificationComponent
                level={verificationState?.level}
                onComplete={() => {
                  verificationState.complete()
                  setVerificationState(undefined)
                }}
                onCancel={() => {
                  verificationState.cancel()
                  setVerificationState(undefined)
                }}
              />
            )}
          </div>
        )
      }
      ```
    </If>

    <If sdk="remix">
      ```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
      import { useReverification, useUser } from '@clerk/remix'
      import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
      import { useState } from 'react'
      import { SessionVerificationLevel } from '@clerk/types'
      import { VerificationComponent } from './VerificationComponent'

      export function UpdateUserEmail() {
        // Use `useUser()` to get the current user's `User` object
        // `User` includes the `update()` method to update the user's primary email address
        const { user } = useUser()

        // TODO: Update to use exported type once available
        const [verificationState, setVerificationState] = useState<
          | {
              complete: () => void
              cancel: () => void
              level: SessionVerificationLevel | undefined
              inProgress: boolean
            }
          | undefined
        >(undefined)

        // Use `useReverification()` to enhance the `update()` method
        // to handle the reverification process
        const changePrimaryEmail = useReverification(
          (emailAddressId: string) => user?.update({ primaryEmailAddressId: emailAddressId }),
          {
            onNeedsReverification: ({ complete, cancel, level }) => {
              setVerificationState({
                complete,
                cancel,
                level,
                inProgress: true,
              })
            },
          },
        )

        const handleClick = async (emailAddressId: string) => {
          try {
            await changePrimaryEmail(emailAddressId)
          } catch (e) {
            // Handle if user cancels the reverification process
            if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
              console.error('User cancelled reverification', e.code)
            }

            // Handle other errors
            // See https://clerk.com/docs/custom-flows/error-handling
            // for more info on error handling
            console.error(JSON.stringify(e, null, 2))
          }
        }

        return (
          <div>
            <span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>

            <ul>
              {user?.emailAddresses.map((email) => (
                <li key={email.id}>
                  <span>{email.emailAddress}</span>
                  {user?.primaryEmailAddressId !== email.id && (
                    <button onClick={() => handleClick(email.id)}>Make primary</button>
                  )}
                </li>
              ))}
            </ul>

            {verificationState?.inProgress && (
              <VerificationComponent
                level={verificationState?.level}
                onComplete={() => {
                  verificationState.complete()
                  setVerificationState(undefined)
                }}
                onCancel={() => {
                  verificationState.cancel()
                  setVerificationState(undefined)
                }}
              />
            )}
          </div>
        )
      }
      ```
    </If>

    <If sdk="tanstack-react-start">
      ```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
      import { useReverification, useUser } from '@clerk/tanstack-react-start'
      import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
      import { useState } from 'react'
      import { SessionVerificationLevel } from '@clerk/types'
      import { VerificationComponent } from './VerificationComponent'

      export function UpdateUserEmail() {
        // Use `useUser()` to get the current user's `User` object
        // `User` includes the `update()` method to update the user's primary email address
        const { user } = useUser()

        // TODO: Update to use exported type once available
        const [verificationState, setVerificationState] = useState<
          | {
              complete: () => void
              cancel: () => void
              level: SessionVerificationLevel | undefined
              inProgress: boolean
            }
          | undefined
        >(undefined)

        // Use `useReverification()` to enhance the `update()` method
        // to handle the reverification process
        const changePrimaryEmail = useReverification(
          (emailAddressId: string) => user?.update({ primaryEmailAddressId: emailAddressId }),
          {
            onNeedsReverification: ({ complete, cancel, level }) => {
              setVerificationState({
                complete,
                cancel,
                level,
                inProgress: true,
              })
            },
          },
        )

        const handleClick = async (emailAddressId: string) => {
          try {
            await changePrimaryEmail(emailAddressId)
          } catch (e) {
            // Handle if user cancels the reverification process
            if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
              console.error('User cancelled reverification', e.code)
            }

            // Handle other errors
            // See https://clerk.com/docs/custom-flows/error-handling
            // for more info on error handling
            console.error(JSON.stringify(e, null, 2))
          }
        }

        return (
          <div>
            <span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>

            <ul>
              {user?.emailAddresses.map((email) => (
                <li key={email.id}>
                  <span>{email.emailAddress}</span>
                  {user?.primaryEmailAddressId !== email.id && (
                    <button onClick={() => handleClick(email.id)}>Make primary</button>
                  )}
                </li>
              ))}
            </ul>

            {verificationState?.inProgress && (
              <VerificationComponent
                level={verificationState?.level}
                onComplete={() => {
                  verificationState.complete()
                  setVerificationState(undefined)
                }}
                onCancel={() => {
                  verificationState.cancel()
                  setVerificationState(undefined)
                }}
              />
            )}
          </div>
        )
      }
      ```
    </If>

    <If sdk="expo">
      ```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
      import { useReverification, useUser } from '@clerk/clerk-expo'
      import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
      import { useState } from 'react'
      import { SessionVerificationLevel } from '@clerk/types'
      import { VerificationComponent } from './VerificationComponent'
      import { Text, View, TouchableOpacity, ScrollView } from 'react-native'

      export function UpdateUserEmail() {
        // Use `useUser()` to get the current user's `User` object
        // `User` includes the `update()` method to update the user's primary email address
        const { user } = useUser()

        // TODO: Update to use exported type once available
        const [verificationState, setVerificationState] = useState<
          | {
              complete: () => void
              cancel: () => void
              level: SessionVerificationLevel | undefined
              inProgress: boolean
            }
          | undefined
        >(undefined)

        // Use `useReverification()` to enhance the `update()` method
        // to handle the reverification process
        const changePrimaryEmail = useReverification(
          (emailAddressId: string) => user?.update({ primaryEmailAddressId: emailAddressId }),
          {
            onNeedsReverification: ({ complete, cancel, level }) => {
              setVerificationState({
                complete,
                cancel,
                level,
                inProgress: true,
              })
            },
          },
        )

        const handleClick = async (emailAddressId: string) => {
          try {
            await changePrimaryEmail(emailAddressId)
          } catch (e) {
            // Handle if user cancels the reverification process
            if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
              console.error('User cancelled reverification', e.code)
            }

            // Handle other errors
            // See https://clerk.com/docs/custom-flows/error-handling
            // for more info on error handling
            console.error(JSON.stringify(e, null, 2))
          }
        }

        return (
          <View>
            <Text>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</Text>

            <ScrollView>
              {user?.emailAddresses.map((email) => (
                <View key={user.id}>
                  <Text>{email.emailAddress}</Text>
                  {user?.primaryEmailAddressId !== email.id && (
                    <TouchableOpacity onPress={() => handleClick(email.id)}>
                      <Text>Make primary</Text>
                    </TouchableOpacity>
                  )}
                </View>
              ))}
            </ScrollView>

            {verificationState?.inProgress && (
              <VerificationComponent
                level={verificationState?.level}
                onComplete={() => {
                  verificationState.complete()
                  setVerificationState(undefined)
                }}
                onCancel={() => {
                  verificationState.cancel()
                  setVerificationState(undefined)
                }}
              />
            )}
          </View>
        )
      }
      ```
    </If>
  </Tab>

  <Tab>
    The `<VerificationComponent />` component handles the reverification process. It uses the `level` property to determine the verification level, which is set to `first_factor`. First, it finds the determined starting first factor from the supported first factors. Then, it prepares the first factor verification using the `strategy` (`email_code` in this case) and `emailAddressId` properties. Finally, it attempts to verify the session with email code provided by the user. If the verification is successful, the `onComplete()` handler is called to complete the reverification process.

    <If sdk="react">
      ```tsx {{ filename: 'src/components/VerificationComponent.tsx', collapsible: true }}
      import { useEffect, useRef, useState } from 'react'
      import { useSession } from '@clerk/clerk-react'
      import {
        EmailCodeFactor,
        SessionVerificationLevel,
        SessionVerificationResource,
      } from '@clerk/types'

      export function VerificationComponent({
        level = 'first_factor',
        onComplete,
        onCancel,
      }: {
        level: SessionVerificationLevel | undefined
        onComplete: () => void
        onCancel: () => void
      }) {
        const { session } = useSession()
        const [code, setCode] = useState<string>('')
        const reverificationRef = useRef<SessionVerificationResource | undefined>(undefined)
        const [determinedStartingFirstFactor, setDeterminedStartingFirstFactor] = useState<
          EmailCodeFactor | undefined
        >()

        useEffect(() => {
          if (reverificationRef.current) {
            return
          }

          session?.startVerification({ level }).then(async (response) => {
            reverificationRef.current = response
            await prepareEmailVerification(response)
          })
        }, [])

        const prepareEmailVerification = async (verificationResource: SessionVerificationResource) => {
          // To simplify the example we will only handle the first factor verification
          if (verificationResource.status === 'needs_first_factor') {
            // Determine the starting first factor from the supported first factors
            const determinedStartingFirstFactor = verificationResource.supportedFirstFactors?.filter(
              (factor) => factor.strategy === 'email_code',
            )[0]

            if (determinedStartingFirstFactor) {
              setDeterminedStartingFirstFactor(determinedStartingFirstFactor)
              // Prepare the first factor verification with the determined starting first factor
              await session?.prepareFirstFactorVerification({
                strategy: determinedStartingFirstFactor.strategy,
                emailAddressId: determinedStartingFirstFactor?.emailAddressId,
              })
            }
          }
        }

        const handleVerificationAttempt = async () => {
          try {
            // Attempt to verify the session with the provided code
            await session?.attemptFirstFactorVerification({
              strategy: 'email_code',
              code,
            })
            onComplete()
          } catch (e) {
            // Any error from the attempt to verify the session can be handled here
            console.error('Error verifying session', e)
          }
        }

        if (!determinedStartingFirstFactor) {
          return null
        }

        return (
          <div>
            <p>Enter verification code sent to {determinedStartingFirstFactor.safeIdentifier || ''}</p>
            <input type="number" name="code" onChange={(e) => setCode(e.target.value)} />
            <button onClick={async () => handleVerificationAttempt()}>Complete</button>
            <button onClick={() => onCancel()}>Cancel</button>
          </div>
        )
      }
      ```
    </If>

    <If sdk="nextjs">
      ```tsx {{ filename: 'components/VerificationComponent.tsx', collapsible: true }}
      'use client'

      import { useEffect, useRef, useState } from 'react'
      import { useSession } from '@clerk/nextjs'
      import {
        EmailCodeFactor,
        SessionVerificationLevel,
        SessionVerificationResource,
      } from '@clerk/types'

      export function VerificationComponent({
        level = 'first_factor',
        onComplete,
        onCancel,
      }: {
        level: SessionVerificationLevel | undefined
        onComplete: () => void
        onCancel: () => void
      }) {
        const { session } = useSession()
        const [code, setCode] = useState<string>('')
        const reverificationRef = useRef<SessionVerificationResource | undefined>(undefined)
        const [determinedStartingFirstFactor, setDeterminedStartingFirstFactor] = useState<
          EmailCodeFactor | undefined
        >()

        useEffect(() => {
          if (reverificationRef.current) {
            return
          }

          session?.startVerification({ level }).then(async (response) => {
            reverificationRef.current = response
            await prepareEmailVerification(response)
          })
        }, [])

        const prepareEmailVerification = async (verificationResource: SessionVerificationResource) => {
          // To simplify the example we will only handle the first factor verification
          if (verificationResource.status === 'needs_first_factor') {
            // Determine the starting first factor from the supported first factors
            const determinedStartingFirstFactor = verificationResource.supportedFirstFactors?.filter(
              (factor) => factor.strategy === 'email_code',
            )[0]

            if (determinedStartingFirstFactor) {
              setDeterminedStartingFirstFactor(determinedStartingFirstFactor)
              // Prepare the first factor verification with the determined starting first factor
              await session?.prepareFirstFactorVerification({
                strategy: determinedStartingFirstFactor.strategy,
                emailAddressId: determinedStartingFirstFactor?.emailAddressId,
              })
            }
          }
        }

        const handleVerificationAttempt = async () => {
          try {
            // Attempt to verify the session with the provided code
            await session?.attemptFirstFactorVerification({
              strategy: 'email_code',
              code,
            })
            onComplete()
          } catch (e) {
            // Any error from the attempt to verify the session can be handled here
            console.error('Error verifying session', e)
          }
        }

        if (!determinedStartingFirstFactor) {
          return null
        }

        return (
          <div>
            <p>Enter verification code sent to {determinedStartingFirstFactor.safeIdentifier || ''}</p>
            <input type="number" name="code" onChange={(e) => setCode(e.target.value)} />
            <button onClick={async () => handleVerificationAttempt()}>Complete</button>
            <button onClick={() => onCancel()}>Cancel</button>
          </div>
        )
      }
      ```
    </If>

    <If sdk="react-router">
      ```tsx {{ filename: 'components/VerificationComponent.tsx', collapsible: true }}
      import { useEffect, useRef, useState } from 'react'
      import { useSession } from '@clerk/react-router'
      import type {
        EmailCodeFactor,
        SessionVerificationLevel,
        SessionVerificationResource,
      } from '@clerk/types'

      export function VerificationComponent({
        level = 'first_factor',
        onComplete,
        onCancel,
      }: {
        level: SessionVerificationLevel | undefined
        onComplete: () => void
        onCancel: () => void
      }) {
        const { session } = useSession()
        const [code, setCode] = useState<string>('')
        const reverificationRef = useRef<SessionVerificationResource | undefined>(undefined)
        const [determinedStartingFirstFactor, setDeterminedStartingFirstFactor] = useState<
          EmailCodeFactor | undefined
        >()

        useEffect(() => {
          if (reverificationRef.current) {
            return
          }

          session?.startVerification({ level }).then(async (response) => {
            reverificationRef.current = response
            await prepareEmailVerification(response)
          })
        }, [])

        const prepareEmailVerification = async (verificationResource: SessionVerificationResource) => {
          // To simplify the example we will only handle the first factor verification
          if (verificationResource.status === 'needs_first_factor') {
            // Determine the starting first factor from the supported first factors
            const determinedStartingFirstFactor = verificationResource.supportedFirstFactors?.filter(
              (factor) => factor.strategy === 'email_code',
            )[0]

            if (determinedStartingFirstFactor) {
              setDeterminedStartingFirstFactor(determinedStartingFirstFactor)
              // Prepare the first factor verification with the determined starting first factor
              await session?.prepareFirstFactorVerification({
                strategy: determinedStartingFirstFactor.strategy,
                emailAddressId: determinedStartingFirstFactor?.emailAddressId,
              })
            }
          }
        }

        const handleVerificationAttempt = async () => {
          try {
            // Attempt to verify the session with the provided code
            await session?.attemptFirstFactorVerification({
              strategy: 'email_code',
              code,
            })
            onComplete()
          } catch (e) {
            // Any error from the attempt to verify the session can be handled here
            console.error('Error verifying session', e)
          }
        }

        if (!determinedStartingFirstFactor) {
          return null
        }

        return (
          <div>
            <p>Enter verification code sent to {determinedStartingFirstFactor.safeIdentifier || ''}</p>
            <input type="number" name="code" onChange={(e) => setCode(e.target.value)} />
            <button onClick={async () => handleVerificationAttempt()}>Complete</button>
            <button onClick={() => onCancel()}>Cancel</button>
          </div>
        )
      }
      ```
    </If>

    <If sdk="chrome-extension">
      ```tsx {{ filename: 'components/VerificationComponent.tsx', collapsible: true }}
      import { useEffect, useRef, useState } from 'react'
      import { useSession } from '@clerk/chrome-extension'
      import type {
        EmailCodeFactor,
        SessionVerificationLevel,
        SessionVerificationResource,
      } from '@clerk/types'

      export function VerificationComponent({
        level = 'first_factor',
        onComplete,
        onCancel,
      }: {
        level: SessionVerificationLevel | undefined
        onComplete: () => void
        onCancel: () => void
      }) {
        const { session } = useSession()
        const [code, setCode] = useState<string>('')
        const reverificationRef = useRef<SessionVerificationResource | undefined>(undefined)
        const [determinedStartingFirstFactor, setDeterminedStartingFirstFactor] = useState<
          EmailCodeFactor | undefined
        >()

        useEffect(() => {
          if (reverificationRef.current) {
            return
          }

          session?.startVerification({ level }).then(async (response) => {
            reverificationRef.current = response
            await prepareEmailVerification(response)
          })
        }, [])

        const prepareEmailVerification = async (verificationResource: SessionVerificationResource) => {
          // To simplify the example we will only handle the first factor verification
          if (verificationResource.status === 'needs_first_factor') {
            // Determine the starting first factor from the supported first factors
            const determinedStartingFirstFactor = verificationResource.supportedFirstFactors?.filter(
              (factor) => factor.strategy === 'email_code',
            )[0]

            if (determinedStartingFirstFactor) {
              setDeterminedStartingFirstFactor(determinedStartingFirstFactor)
              // Prepare the first factor verification with the determined starting first factor
              await session?.prepareFirstFactorVerification({
                strategy: determinedStartingFirstFactor.strategy,
                emailAddressId: determinedStartingFirstFactor?.emailAddressId,
              })
            }
          }
        }

        const handleVerificationAttempt = async () => {
          try {
            // Attempt to verify the session with the provided code
            await session?.attemptFirstFactorVerification({
              strategy: 'email_code',
              code,
            })
            onComplete()
          } catch (e) {
            // Any error from the attempt to verify the session can be handled here
            console.error('Error verifying session', e)
          }
        }

        if (!determinedStartingFirstFactor) {
          return null
        }

        return (
          <div>
            <p>Enter verification code sent to {determinedStartingFirstFactor.safeIdentifier || ''}</p>
            <input type="number" name="code" onChange={(e) => setCode(e.target.value)} />
            <button onClick={async () => handleVerificationAttempt()}>Complete</button>
            <button onClick={() => onCancel()}>Cancel</button>
          </div>
        )
      }
      ```
    </If>

    <If sdk="remix">
      ```tsx {{ filename: 'components/VerificationComponent.tsx', collapsible: true }}
      import { useEffect, useRef, useState } from 'react'
      import { useSession } from '@clerk/remix'
      import {
        EmailCodeFactor,
        SessionVerificationLevel,
        SessionVerificationResource,
      } from '@clerk/types'

      export function VerificationComponent({
        level = 'first_factor',
        onComplete,
        onCancel,
      }: {
        level: SessionVerificationLevel | undefined
        onComplete: () => void
        onCancel: () => void
      }) {
        const { session } = useSession()
        const [code, setCode] = useState<string>('')
        const reverificationRef = useRef<SessionVerificationResource | undefined>(undefined)
        const [determinedStartingFirstFactor, setDeterminedStartingFirstFactor] = useState<
          EmailCodeFactor | undefined
        >()

        useEffect(() => {
          if (reverificationRef.current) {
            return
          }

          session?.startVerification({ level }).then(async (response) => {
            reverificationRef.current = response
            await prepareEmailVerification(response)
          })
        }, [])

        const prepareEmailVerification = async (verificationResource: SessionVerificationResource) => {
          // To simplify the example we will only handle the first factor verification
          if (verificationResource.status === 'needs_first_factor') {
            // Determine the starting first factor from the supported first factors
            const determinedStartingFirstFactor = verificationResource.supportedFirstFactors?.filter(
              (factor) => factor.strategy === 'email_code',
            )[0]

            if (determinedStartingFirstFactor) {
              setDeterminedStartingFirstFactor(determinedStartingFirstFactor)
              // Prepare the first factor verification with the determined starting first factor
              await session?.prepareFirstFactorVerification({
                strategy: determinedStartingFirstFactor.strategy,
                emailAddressId: determinedStartingFirstFactor?.emailAddressId,
              })
            }
          }
        }

        const handleVerificationAttempt = async () => {
          try {
            // Attempt to verify the session with the provided code
            await session?.attemptFirstFactorVerification({
              strategy: 'email_code',
              code,
            })
            onComplete()
          } catch (e) {
            // Any error from the attempt to verify the session can be handled here
            console.error('Error verifying session', e)
          }
        }

        if (!determinedStartingFirstFactor) {
          return null
        }

        return (
          <div>
            <p>Enter verification code sent to {determinedStartingFirstFactor.safeIdentifier || ''}</p>
            <input type="number" name="code" onChange={(e) => setCode(e.target.value)} />
            <button onClick={async () => handleVerificationAttempt()}>Complete</button>
            <button onClick={() => onCancel()}>Cancel</button>
          </div>
        )
      }
      ```
    </If>

    <If sdk="tanstack-react-start">
      ```tsx {{ filename: 'components/VerificationComponent.tsx', collapsible: true }}
      import { useEffect, useRef, useState } from 'react'
      import { useSession } from '@clerk/tanstack-react-start'
      import {
        EmailCodeFactor,
        SessionVerificationLevel,
        SessionVerificationResource,
      } from '@clerk/types'

      export function VerificationComponent({
        level = 'first_factor',
        onComplete,
        onCancel,
      }: {
        level: SessionVerificationLevel | undefined
        onComplete: () => void
        onCancel: () => void
      }) {
        const { session } = useSession()
        const [code, setCode] = useState<string>('')
        const reverificationRef = useRef<SessionVerificationResource | undefined>(undefined)
        const [determinedStartingFirstFactor, setDeterminedStartingFirstFactor] = useState<
          EmailCodeFactor | undefined
        >()

        useEffect(() => {
          if (reverificationRef.current) {
            return
          }

          session?.startVerification({ level }).then(async (response) => {
            reverificationRef.current = response
            await prepareEmailVerification(response)
          })
        }, [])

        const prepareEmailVerification = async (verificationResource: SessionVerificationResource) => {
          // To simplify the example we will only handle the first factor verification
          if (verificationResource.status === 'needs_first_factor') {
            // Determine the starting first factor from the supported first factors
            const determinedStartingFirstFactor = verificationResource.supportedFirstFactors?.filter(
              (factor) => factor.strategy === 'email_code',
            )[0]

            if (determinedStartingFirstFactor) {
              setDeterminedStartingFirstFactor(determinedStartingFirstFactor)
              // Prepare the first factor verification with the determined starting first factor
              await session?.prepareFirstFactorVerification({
                strategy: determinedStartingFirstFactor.strategy,
                emailAddressId: determinedStartingFirstFactor?.emailAddressId,
              })
            }
          }
        }

        const handleVerificationAttempt = async () => {
          try {
            // Attempt to verify the session with the provided code
            await session?.attemptFirstFactorVerification({
              strategy: 'email_code',
              code,
            })
            onComplete()
          } catch (e) {
            // Any error from the attempt to verify the session can be handled here
            console.error('Error verifying session', e)
          }
        }

        if (!determinedStartingFirstFactor) {
          return null
        }

        return (
          <div>
            <p>Enter verification code sent to {determinedStartingFirstFactor.safeIdentifier || ''}</p>
            <input type="number" name="code" onChange={(e) => setCode(e.target.value)} />
            <button onClick={async () => handleVerificationAttempt()}>Complete</button>
            <button onClick={() => onCancel()}>Cancel</button>
          </div>
        )
      }
      ```
    </If>

    <If sdk="expo">
      ```tsx {{ filename: 'components/VerificationComponent.tsx', collapsible: true }}
      import { useEffect, useRef, useState } from 'react'
      import { useSession } from '@clerk/clerk-expo'
      import {
        EmailCodeFactor,
        SessionVerificationLevel,
        SessionVerificationResource,
      } from '@clerk/types'
      import { Text, View, TouchableOpacity, TextInput } from 'react-native'

      export function VerificationComponent({
        level = 'first_factor',
        onComplete,
        onCancel,
      }: {
        level: SessionVerificationLevel | undefined
        onComplete: () => void
        onCancel: () => void
      }) {
        const { session } = useSession()
        const [code, setCode] = useState<string>('')
        const reverificationRef = useRef<SessionVerificationResource | undefined>(undefined)
        const [determinedStartingFirstFactor, setDeterminedStartingFirstFactor] = useState<
          EmailCodeFactor | undefined
        >()

        useEffect(() => {
          if (reverificationRef.current) {
            return
          }

          session?.startVerification({ level }).then(async (response) => {
            reverificationRef.current = response
            await prepareEmailVerification(response)
          })
        }, [])

        const prepareEmailVerification = async (verificationResource: SessionVerificationResource) => {
          // To simplify the example we will only handle the first factor verification
          if (verificationResource.status === 'needs_first_factor') {
            // Determine the starting first factor from the supported first factors
            const determinedStartingFirstFactor = verificationResource.supportedFirstFactors?.filter(
              (factor) => factor.strategy === 'email_code',
            )[0]

            if (determinedStartingFirstFactor) {
              setDeterminedStartingFirstFactor(determinedStartingFirstFactor)
              // Prepare the first factor verification with the determined starting first factor
              await session?.prepareFirstFactorVerification({
                strategy: determinedStartingFirstFactor.strategy,
                emailAddressId: determinedStartingFirstFactor?.emailAddressId,
              })
            }
          }
        }

        const handleVerificationAttempt = async () => {
          try {
            // Attempt to verify the session with the provided code
            await session?.attemptFirstFactorVerification({
              strategy: 'email_code',
              code,
            })
            onComplete()
          } catch (e) {
            // Any error from the attempt to verify the session can be handled here
            console.error('Error verifying session', e)
          }
        }

        if (!determinedStartingFirstFactor) {
          return null
        }

        return (
          <View>
            <Text>
              Enter verification code sent to {determinedStartingFirstFactor.safeIdentifier || ''}
            </Text>
            <TextInput
              keyboardType="numeric"
              value={code}
              onChangeText={setCode}
              placeholder="Enter code"
            />
            <TouchableOpacity onPress={async () => handleVerificationAttempt()}>
              <Text>Complete</Text>
            </TouchableOpacity>
            <TouchableOpacity onPress={() => onCancel()}>
              <Text>Cancel</Text>
            </TouchableOpacity>
          </View>
        )
      }
      ```
    </If>
  </Tab>
</Tabs>
</Tab> </Tabs>

Properties

<Properties> - `fetcher` - `Fetcher extends (...args: any[]) => Promise<any>`

A function that returns a promise.


The optional options object. </Properties>

UseReverificationOptions

<Properties> - `onNeedsReverification?` - <code>(\{ complete, cancel, level }: [NeedsReverificationParameters](#needs-reverification-parameters)) => void</code>

Handler for the reverification process. Opts out of using the default UI. Use this to build a custom UI. </Properties>

NeedsReverificationParameters

<Properties> - `complete` - `() => void`

Marks the reverification process as complete and retries the original request.


  • cancel
  • () => void

Marks the reverification process as cancelled and rejects the original request.


  • level
  • "first_factor" | "second_factor" | "multi_factor" | undefined

The verification level required for the reverification process. </Properties>

ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•‘
ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•

← Root | ↑ Up