npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ronas-it/clerk-react-native-hooks

v0.2.0

Published

Hooks and helpers for Clerk authentication in React Native / Expo apps

Readme

@ronas-it/clerk-react-native-hooks

Hooks and helpers for user authentication with Clerk Expo SDK.

Install

npm install @ronas-it/clerk-react-native-hooks @clerk/expo @clerk/types expo-web-browser expo-auth-session expo-secure-store

react, react-native, and your Clerk/Expo versions should match what @clerk/clerk-expo expects for your Expo SDK.

In your app, follow Clerk's Expo quickstart: wrap the root layout with ClerkProvider from @clerk/expo and pass your publishable key from the Clerk Dashboard.

import { ClerkProvider } from '@clerk/expo';
import { Slot } from 'expo-router';

export default function RootLayout() {
  return (
    <ClerkProvider publishableKey={publishableKey}>
      <Slot />
    </ClerkProvider>
  );
}

Then import the hooks you need:

import { useAuthWithIdentifier, useOtpVerification, useClerkResources } from '@ronas-it/clerk-react-native-hooks';
import type { OtpStrategy } from '@ronas-it/clerk-react-native-hooks';

API

useClerkResources

Returns core Clerk resources used by the higher-level hooks.

Returns:

  • signUpSignUp
  • signInSignIn
  • signOut — signs out the current user

useAuthWithIdentifier

Sign up or sign in with an identifier (emailAddress, phoneNumber, or username) using either OTP or password.

Parameters:

  • method'emailAddress', 'phoneNumber', or 'username'
  • verifyBy'otp' or 'password'

Returns:

  • startSignUp, startSignIn, isLoading
  • For email/phone + OTP: verifyCode, isVerifying (verifyCode takes code, optional tokenTemplate; set isSignUp: true only on the sign-up path, omit for sign-in)
  • For OTP sign-in only: startSignIn accepts optional signUpIfMissing (see Clerk: Sign-in-or-up with signUpIfMissing)

Examples

Passwordless sign-up and sign-in:

const { startSignUp, startSignIn, verifyCode } = useAuthWithIdentifier('emailAddress', 'otp');
// useAuthWithIdentifier('phoneNumber', 'otp') for SMS

await startSignUp({ identifier }); // new user
await verifyCode({ code, isSignUp: true, tokenTemplate });

await startSignIn({ identifier }); // existing user
await verifyCode({ code, tokenTemplate });
// useOtpVerification — same channel as email/phone: pass 'email_code' or 'phone_code' once; sendOtpCode = resend
const { verifyCode, sendOtpCode } = useOtpVerification('email_code'); // SMS: useOtpVerification('phone_code')
await sendOtpCode({ isSignUp: true });
await verifyCode({ code, isSignUp: true, tokenTemplate });

tokenTemplate (optional) is the name of a JWT template from the Clerk Dashboard. When set, Clerk issues a JWT built from that template so you control which claims are included. Omit it if you only need the default session token.

Password-based sign-up and sign-in:

// Sign-up: password + profile — OTP screen next (code is sent inside startSignUp when verifyBy is 'otp')
const { startSignUp, isLoading } = useAuthWithIdentifier('emailAddress', 'otp');

const onSignUp = async (values: { emailAddress: string; password: string; firstName: string; lastName: string }) => {
  const { isSuccess, error } = await startSignUp({
    identifier: values.emailAddress,
    password: values.password,
    firstName: values.firstName,
    lastName: values.lastName,
  });

  if (isSuccess) {
    // go to the email OTP step
  }

  if (error) {
    showToast(error?.longMessage);
  }
};
// Same flow but verifyBy: 'password' — send/verify OTP with useOtpVerification (not inside startSignUp)
const { startSignUp, isLoading } = useAuthWithIdentifier('emailAddress', 'password');
const { sendOtpCode } = useOtpVerification('email_code');

await startSignUp({ identifier: values.emailAddress, password: values.password });
await sendOtpCode({ isSignUp: true });
// Sign-in: email and password
const { startSignIn, isLoading } = useAuthWithIdentifier('emailAddress', 'password');

const onSignIn = async (values: { emailAddress: string; password: string }) => {
  const { isSuccess, error } = await startSignIn({
    identifier: values.emailAddress,
    password: values.password,
    tokenTemplate: 'your_jwt_template',
  });

  if (isSuccess) {
    // handle success
  }

  if (error) {
    showToast(error?.longMessage);
  }
};

Single entry: one email/phone field, then OTP (sign-in or sign-up):

Use this when sign-in and sign-up share one identifier (email or phone). Prefer Clerk’s **signUpIfMissing** flow so verification runs even if the account does not exist yet. See Clerk: [Sign-in-or-up with signUpIfMissing](https://clerk.com/docs/guides/development/custom-flows/authentication/sign-in-or-up#sign-in-or-up-with-sign-up-if-missing).

// First screen — treat as sign-in with signUpIfMissing; OTP is sent on success
const { startSignIn, isLoading } = useAuthWithIdentifier('emailAddress', 'otp');

const onContinue = async (email: string) => {
  const { error, isSuccess } = await startSignIn({ identifier: email, signUpIfMissing: true });

  if (isSuccess) {
    // go to OTP step (same hook instance keeps signIn / signUp resources)
  }

  if (error) {
    showToast(error?.longMessage);
  }
};
// OTP screen
const { verifyCode, isVerifying } = useAuthWithIdentifier('emailAddress', 'otp');

const onVerify = async (code: string) => {
  const { sessionToken, error, isSuccess, signUp } = await verifyCode({
    code,
    tokenTemplate: 'your_jwt_template',
  });

  if (isSuccess) {
    // handle success
  }

  if (error) {
    showToast(error?.longMessage);
  }
};

On the code screen you can use useOtpVerification for resend / verify instead; use **isSignUp: false** for sendOtpCode and verifyCode on this path so transfer behavior stays correct.

useAuthWithSSO

Hook for SSO flows.

  • startSSOFlowstrategy, redirectUrl, optional tokenTemplate
  • isLoading

Example

import * as AuthSession from 'expo-auth-session';

const { startSSOFlow, isLoading } = useAuthWithSSO();

const onGooglePress = async () => {
  const { isSuccess, error } = await startSSOFlow({
    strategy: 'oauth_google',
    redirectUrl: AuthSession.makeRedirectUri({ path: navigationConfig.auth.signUp }),
    tokenTemplate: 'your_jwt_template', // optional
  });

  if (isSuccess) {
    // handle success
  }

  if (error) {
    showToast(error?.longMessage);
  }
};

Use another OAuthStrategy (for example oauth_github) the same way.

useAuthWithTicket

Ticket-based auth: your backend obtains a sign-in token from Clerk (Backend API / SDK) and returns it to the app.

  • startAuthorizationticket, optional tokenTemplate
  • isLoading

Example

const { startAuthorization, isLoading } = useAuthWithTicket();

const signInWithBackendTicket = async () => {
  const { ticket } = await yourApi.issueClerkSignInToken(); // server calls Clerk, returns the token to the client

  const { error, isSuccess } = await startAuthorization({
    ticket,
    tokenTemplate: 'your_jwt_template', // optional
  });

  if (isSuccess) {
    // handle success
  }

  if (error) {
    showToast(error?.longMessage);
  }
};

useAddIdentifier

Link an extra email or phone to the signed-in user. createIdentifier attaches the value (or resumes verification if it already exists) and sends a one-time code; verifyCode confirms with the same identifier string. To resend the code, call createIdentifier again with the same identifier (there is no separate resend method).

  • type'email' or 'phone'
  • createIdentifier{ identifier }
  • verifyCode{ code, identifier }
  • isCreating, isVerifying

Example

// Step 1 — register identifier and trigger email code / SMS
const { createIdentifier, isCreating } = useAddIdentifier('email');

const onSaveEmail = async (email: string) => {
  const { isSuccess, error } = await createIdentifier({ identifier: email });

  if (isSuccess) {
    // go to verification; keep `email` for step 2
  }
};
// Step 2 — same `identifier` as in createIdentifier
const { verifyCode, isVerifying } = useAddIdentifier('email');

const onSubmitCode = async (code: string, email: string) => {
  const { isSuccess, error } = await verifyCode({ code, identifier: email });

  if (isSuccess) {
    // handle success
  }
};
// Resend — same as the first send: createIdentifier({ identifier }) again
const { createIdentifier, isCreating } = useAddIdentifier('email');

const onResendCode = async (email: string) => {
  await createIdentifier({ identifier: email });
};

useUpdateIdentifier

For a signed-in user who is changing their primary email or phone: createIdentifier adds the new address and sends a verification code; after verifyCode succeeds, that identifier becomes primary and the previous primary is removed. To resend the code, call createIdentifier again with the same new identifier.

  • type'email' or 'phone'
  • createIdentifier{ identifier }
  • verifyCode{ code, identifier }
  • isCreating, isVerifying, isUpdating (isUpdating covers the primary swap + cleanup)

Example

// Step 1 — send code to the new address
const { createIdentifier, isCreating } = useUpdateIdentifier('email');

const onSaveNewEmail = async (newEmail: string) => {
  const { isSuccess, error } = await createIdentifier({ identifier: newEmail });

  if (isSuccess) {
    // go to verification; keep `newEmail` for step 2
  }
};
// Step 2 — same `identifier` as in createIdentifier
const { verifyCode, isVerifying, isUpdating } = useUpdateIdentifier('email');

const onSubmitCode = async (code: string, newEmail: string) => {
  const { isSuccess, error } = await verifyCode({ code, identifier: newEmail.trim() });

  if (isSuccess) {
    // handle success
  }
};
// Resend — same as the first send: createIdentifier({ identifier }) again
const { createIdentifier, isCreating } = useUpdateIdentifier('email');

const onResendCode = async (newEmail: string) => {
  await createIdentifier({ identifier: newEmail });
};

useOtpVerification

Lower-level OTP send/verify for sign-in and sign-up.

  • First argument — 'email_code' or 'phone_code': fixed for the hook instance (same as email vs phone in useAuthWithIdentifier).

sendOtpCode asks Clerk to deliver a code (first send or resend). verifyCode submits the code, activates the session, and optionally returns a JWT. Pass isSignUp: true only for sign-up; omit for sign-in (default).

  • sendOtpCode{ isSignUp?, isSecondFactor? } (both optional; sign-in and 2FA omit isSignUp or set false)
  • verifyCode{ code, isSignUp?, tokenTemplate?, isSecondFactor? }
  • isVerifying — true while verifyCode is running

Example

const { verifyCode, isVerifying, sendOtpCode } = useOtpVerification('email_code');

const sendOrResendCode = () => {
  sendOtpCode({ isSignUp: true });
};

const onSubmitCode = async (code: string) => {
  const { isSuccess, error, sessionToken } = await verifyCode({
    code,
    isSignUp: true,
    tokenTemplate: 'your_jwt_template', // optional
  });

  if (isSuccess) {
    // handle success
  }
};

For SMS, use useOtpVerification('phone_code'). For sign-in OTP, omit isSignUp in both sendOtpCode and verifyCode.

useResetPassword

Password reset via email or phone OTP for the forgot-password flow.

Use the same method ('emailAddress' or 'phoneNumber') on useResetPassword for each step. To resend the reset code, call startResetPassword again with the same identifier.

  1. startResetPassword({ identifier }) — sends the reset code (isCodeSending); use the same call again to resend.
  2. verifyCode({ code }) — verifies the code (isVerifying).
  3. resetPassword({ password, tokenTemplate? }) — applies the new password and finishes sign-in (isResetting).

Example

// 1 — request code
const { startResetPassword, isCodeSending } = useResetPassword({ method: 'emailAddress' });

const onRequestCode = async (email: string) => {
  const { isSuccess, error } = await startResetPassword({ identifier: email });
  // if isSuccess → go to code step; keep `email` for resend
};
// 2 — submit code
const { verifyCode, isVerifying } = useResetPassword({ method: 'emailAddress' });

const onSubmitCode = async (code: string) => {
  const { isSuccess, error } = await verifyCode({ code });
  // if isSuccess → go to new-password step
};
// Resend — same as step 1: startResetPassword({ identifier }) again
const { startResetPassword, isCodeSending } = useResetPassword({ method: 'emailAddress' });

const onResendCode = (email: string) => {
  startResetPassword({ identifier: email });
};
// 3 — new password
const { resetPassword, isResetting } = useResetPassword({ method: 'emailAddress' });

const onSetNewPassword = async (password: string) => {
  const { isSuccess, sessionToken, error } = await resetPassword({
    password,
    tokenTemplate: 'your_jwt_template', // optional
  });
  // if isSuccess → handle success
};

For SMS, use { method: 'phoneNumber' } and pass the phone number as identifier.

useUpdatePassword

Change password for the signed-in user using the current password and a new password, not the forgot-password / OTP flow.

  • updatePassword{ currentPassword, newPassword }{ isSuccess, error? }
  • isPasswordUpdating

Example

const { updatePassword, isPasswordUpdating } = useUpdatePassword();

const onSubmit = async (values: { currentPassword: string; newPassword: string }) => {
  const { isSuccess, error } = await updatePassword({
    currentPassword: values.currentPassword,
    newPassword: values.newPassword,
  });

  if (isSuccess) {
    // handle success
  }
};

useGetSessionToken

Helper hook when you need to read the session token outside the higher-level flows.

  • getSessionToken{ tokenTemplate? }{ isSuccess, sessionToken?, error? }

Client Trust / second factor

Client Trust adds one more verification step after a valid password sign-in:

  • needs_second_factor: the password is correct, but Clerk treats this device as new or untrusted and requires an email/SMS code.

If startSignIn returns isSuccess: false, check status, then call sendOtpCode / verifyCode with isSecondFactor: true.

const { startSignIn } = useAuthWithIdentifier('emailAddress', 'password');
const { sendOtpCode, verifyCode } = useOtpVerification('email_code');

const result = await startSignIn({ identifier, password, tokenTemplate });

if (!result.isSuccess && result.status === 'needs_second_factor') {
  await sendOtpCode({ isSecondFactor: true });
  // await verifyCode({ code, isSecondFactor: true, tokenTemplate })
}

Contributing

See CONTRIBUTING.md.