# How to Use AuthView in an Expo App: Native Authentication with Clerk

This is the first part of a two-part series on building a native authentication flow in an Expo app with Clerk's AuthView component. In this part, you will learn why native authentication matters, set up a Clerk application, initialize an Expo project, and build the initial sign-in screen using AuthView. Part 2 will cover the full home screen, managing user profiles, and handling authentication state.

Mobile [authentication](https://clerk.com/docs/guides/how-clerk-works/overview.md) is one of the most complex parts of app development. Traditional approaches force developers to either build custom sign-in flows from scratch — often requiring hundreds of lines of code — or rely on WebView-based components that break the native user experience. Clerk's AuthView component changes this by rendering a fully native authentication UI with roughly five lines of code.

In this tutorial, you will build a complete Expo app with native sign-in and sign-up powered by AuthView. The finished app includes a public home screen, a native authentication screen, and a user profile page with Clerk's `UserButton` and `UserProfileView` components. AuthView is currently in beta — the core API is stable, but check the [Clerk changelog](https://clerk.com/changelog.md) for the latest status.

By the end of this guide, you will understand how AuthView works under the hood, how it synchronizes native sessions with the JavaScript SDK, and why native authentication outperforms WebView-based approaches in both security and user experience.

## What is AuthView?

AuthView is Clerk's native authentication component for Expo. Import it from `@clerk/expo/native` and it renders a complete sign-in and sign-up interface using SwiftUI on iOS and Jetpack Compose on Android. This is not a WebView wrapping a web page — it is a genuinely native UI built with each platform's own design framework.

AuthView automatically handles the full authentication lifecycle based on your Clerk Dashboard configuration. This includes email and password sign-in, email verification codes, [OAuth](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/overview.md) providers like Google and Apple, [passkeys](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options.md#passkeys), [multi-factor authentication (MFA)](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options.md#multi-factor-authentication), and password recovery. When you enable a new authentication method in the Dashboard, AuthView picks it up automatically — no code changes or app updates needed.

AuthView was released in March 2026 with `@clerk/expo` 3.1 ([changelog](https://clerk.com/changelog/2026-03-09-expo-native-components.md)). It has intentionally minimal props:

- `mode` — accepts `"signIn"`, `"signUp"`, or `"signInOrUp"` (default). Determines which authentication flows are shown.
- `isDismissible` — a boolean (default `true`) that shows a dismiss button in the navigation bar. Set it to `false` to require the user to complete authentication before the view can close.
- `onDismiss` — an optional callback that fires when the user dismisses the view, or when the native flow finishes and requests dismissal.
- `logo` — an optional React Native element that replaces the logo configured in the Dashboard. It must define its own layout and accessibility behavior.
- `logoMaxHeight` — the maximum logo height in density-independent pixels (default `44`).

For the current list, see the [AuthView reference](https://clerk.com/docs/reference/expo/native-components/auth-view.md).

This simplicity is by design. Authentication configuration belongs in the Clerk Dashboard, not scattered through your codebase. AuthView requires roughly five lines of code where a custom flow approach needs 25 or more lines per OAuth provider, plus manual state management, error handling, and token exchange logic.

## Why native authentication matters for mobile apps

Native authentication UIs provide meaningful advantages over WebView-based approaches in security, user experience, and conversion rates.

### Security

[RFC 8252](https://datatracker.ietf.org/doc/html/rfc8252) — the IETF standard for OAuth 2.0 in native apps — explicitly states that native apps "MUST NOT use embedded user-agents" (Section 8.12) for authentication. Embedded WebViews expose credentials to the host app, enable phishing by hiding or spoofing the URL bar, and prevent users from verifying the identity of the authentication server.

Google enforces this standard: OAuth sign-in via embedded WebViews [is prohibited](https://developers.google.com/identity/protocols/oauth2/policies#webview), as [announced in 2016](https://developers.googleblog.com/en/modernizing-oauth-interactions-in-native-apps-for-better-usability-and-security/), requiring developers to use Chrome Custom Tabs (Android) or ASWebAuthenticationSession (iOS) instead. An Android WebView [AutoSpill vulnerability](https://www.darkreading.com/cyberattacks-data-breaches/android-vulnerability-leaks-credentials-from-password-managers-) demonstrated the risk by leaking credentials from the top 10 password managers through WebView autofill behavior.

AuthView avoids these risks entirely. It never uses an embedded WebView: on Android, Google Sign-In runs through the native Credential Manager API; on iOS, Apple Sign-In uses the native Sign in with Apple API (`ASAuthorization`); and other providers open in a secure system browser (`ASWebAuthenticationSession` on iOS, Chrome Custom Tabs on Android) rather than an in-app WebView. This matches the security model that platform vendors require. Firebase Auth and Supabase Auth, by contrast, offer no pre-built native UI components for Expo — developers must build their own login screens in React Native and wire up the OAuth flows themselves.

### User experience and conversion

Authentication friction directly impacts conversion. Each additional authentication step reduces conversion by [10–15%](https://mojoauth.com/data-and-research-reports/passwordless-conversion-impact-report-2026/), and [46% of US consumers](https://www.corbado.com/blog/login-friction-kills-conversion) report failing to complete transactions due to authentication problems.

Native authentication UIs avoid embedded WebViews entirely, render instantly without WebView startup time, and provide platform-consistent design that users trust. They also cut down on browser hand-offs — password, passkey, and MFA flows never leave the app, and Google Sign-In on Android runs through Credential Manager — though some OAuth providers still complete in a secure system browser session. They also integrate with biometric authentication natively — [81% of smartphones](https://www.iproov.com/blog/biometric-statistics-70) had biometrics enabled as of 2022 (per Cisco Duo's Trusted Access Report), and Amazon reported [6x faster sign-in](https://www.aboutamazon.com/news/retail/amazon-passwordless-sign-in-passkey) after deploying passkeys to 175 million users.

## What you'll build

The finished app uses Expo Router's file-based routing with a public home screen at the root and two route groups: one for authentication screens and one for protected content.

```text
src/app/
├── _layout.tsx          ← ClerkProvider setup
├── index.tsx            ← Public home screen with conditional content
├── (auth)/
│   ├── _layout.tsx      ← Redirects signed-in users to home
│   └── sign-in.tsx      ← AuthView component
└── (protected)/
    ├── _layout.tsx      ← Redirects signed-out users to sign-in
    └── profile.tsx      ← UserButton + UserProfileView
```

Each screen serves a distinct purpose:

- **`_layout.tsx` (root)** — wraps the entire app with `ClerkProvider` for authentication state management
- **`index.tsx`** — the public home screen. It shows different content based on authentication state using the `Show` component, so it renders for signed-out visitors as well as signed-in users.
- **`(auth)/sign-in.tsx`** — renders AuthView for native sign-in and sign-up
- **`(protected)/profile.tsx`** — displays the user's profile with `UserButton` (avatar with native modal) and `UserProfileView` (inline profile management). The `(protected)` layout guards everything in this group, so only signed-in users reach it.

## Prerequisites

### Tools and accounts needed

Before starting, confirm you have the following:

- **Node.js** — LTS version (20.x or later). Download from [nodejs.org](https://nodejs.org).
- **A Clerk account** — create one at [clerk.com](https://clerk.com/) and set up an application in the [Clerk Dashboard](https://dashboard.clerk.com/sign-in).
- **Xcode** (for iOS) or **Android Studio** (for Android) — at least one is required to run a development build.
- **Basic familiarity with React and TypeScript** — you do not need to be an expert. This tutorial explains each code snippet line by line.

### Why a development build is required

AuthView uses native modules — SwiftUI on iOS and Jetpack Compose on Android — that are compiled into the app binary. These modules are **not** available in Expo Go, which only includes a fixed set of pre-bundled libraries.

A [development build](https://docs.expo.dev/develop/development-builds/introduction/) is a debug version of your app that includes `expo-dev-client` and any custom native modules your project needs. Create one by running `npx expo run:ios` or `npx expo run:android` instead of `npx expo start`.

> Expo Go cannot run AuthView or any other Clerk native component. You must use a development build for this entire tutorial. If you see "Native module not available" errors, you are likely running in Expo Go.

The development build is a one-time setup cost. Once built, JavaScript changes still hot-reload instantly — you only need to rebuild when adding or removing native dependencies.

**Checkpoint:** You should now have Node.js installed, a Clerk account created, and either Xcode or Android Studio set up.

## Setting up the Clerk application

### Create a Clerk application

1. Sign in to the [Clerk Dashboard](https://dashboard.clerk.com/sign-in)
2. Select **[Create application](https://dashboard.clerk.com/apps/new)** (or use an existing one)
3. Choose the authentication methods you want to support — email, password, Google, Apple, or any combination
4. Copy your **Publishable Key** from the **[API keys](https://dashboard.clerk.com/~/api-keys)** section — you will need this in a later step

### Enable the Native API

AuthView communicates with Clerk's backend through native SDK endpoints that must be explicitly enabled.

1. In the Clerk Dashboard, navigate to the **[Native applications](https://dashboard.clerk.com/~/native-applications)** page
2. Toggle **Native API** to enabled

This setting exposes the endpoints that AuthView needs for native sign-in, sign-up, and session management. Without it, native components will fail to authenticate.

### Configure social connections (optional)

If you want Google Sign-In or Apple Sign-In, configure them in the Clerk Dashboard under **[SSO connections → Social](https://dashboard.clerk.com/~/user-authentication/sso-connections/social)**. AuthView handles these flows automatically once they are enabled — you do not need to install additional packages or write custom hooks.

- **Google Sign-In** uses the native Credential Manager API (Sign in with Google) on Android and a secure system browser (`ASWebAuthenticationSession`) on iOS — never an embedded WebView
- **Apple Sign-In** uses the native Sign in with Apple API (`ASAuthorization`) on iOS; on Android, where Apple provides no native SDK, it runs in a secure system browser (Chrome Custom Tabs)

For detailed setup instructions, see the Clerk guides for [Sign in with Google](https://clerk.com/docs/expo/guides/configure/auth-strategies/sign-in-with-google.md) and [Sign in with Apple](https://clerk.com/docs/expo/guides/configure/auth-strategies/sign-in-with-apple.md).

> This tutorial works without any social connections configured. Email and password authentication is sufficient to follow along. You can add social providers later — AuthView picks them up without app-code changes, but native Google and Apple Sign-In still require the one-time platform setup in the guides above (OAuth credentials, native application registration, and a rebuild).

**Checkpoint:** You should now have a Clerk application with authentication methods configured, Native API enabled, and your Publishable Key copied.

## Creating the Expo project

### Initialize a new Expo app

Create a new project using `create-expo-app` with the SDK 55 template:

```bash
npx create-expo-app@latest clerk-expo-authview --template default@sdk-55
```

The `--template default@sdk-55` flag pins the project to Expo SDK 55. Without it, `@latest` may pull a different SDK version during transition periods, causing the template structure to differ from this tutorial.

Navigate into the project directory:

```bash
cd clerk-expo-authview
```

> As of SDK 55, the default template uses a `src/app/` directory structure. All file paths in this tutorial are under `src/app/`. If you are using SDK 54 or earlier, routes are under `app/` instead — adjust paths accordingly. The older template also includes `app/(tabs)/`, `app/modal.tsx`, and `app/+not-found.tsx` instead of the files listed below.

### Remove default template files

The default template includes starter routes you do not need. `src/app/explore.tsx` is unused, and the template's `src/app/index.tsx` pulls in demo components and animation dependencies you are about to uninstall — you will write your own `index.tsx` further down. Remove them both:

```bash
rm -f src/app/index.tsx src/app/explore.tsx
```

Uninstall `react-native-reanimated` and `react-native-worklets` to avoid Android build issues:

```bash
npx expo uninstall react-native-reanimated react-native-worklets
```

Open `src/app/_layout.tsx` and remove the `react-native-reanimated` import line if present. You will replace the full contents of this file in the ClerkProvider setup step.

### Install dependencies

Install the required packages:

```bash
npx expo install @clerk/expo expo-secure-store expo-dev-client
```

Each package serves a specific purpose:

- **`@clerk/expo`** — Clerk's Expo SDK with native component support. Includes AuthView, UserButton, UserProfileView, and all authentication hooks.
- **`expo-secure-store`** — encrypted token storage using iOS Keychain and Android Keystore. Clerk uses this to persist session tokens securely across app restarts.
- **`expo-dev-client`** — enables development builds with custom native modules. Required because AuthView uses SwiftUI and Jetpack Compose code that Expo Go cannot run.

> Some Clerk tutorials also install `expo-auth-session` and `expo-web-browser`. These are optional peer dependencies only needed for browser-based OAuth flows using the `useSSO` or `useOAuth` hooks. AuthView handles OAuth entirely through native platform APIs, so these packages are **not** required for this tutorial.

### Set environment variables

Create a `.env` file in the project root:

```bash
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_your-key-here
```

Replace `pk_test_your-key-here` with the Publishable Key from your Clerk Dashboard.

The `EXPO_PUBLIC_` prefix makes the variable accessible to client-side code. Metro (Expo's bundler) inlines [environment variables](https://clerk.com/docs/guides/development/clerk-environment-variables.md) with this prefix at build time.

> If Metro bundler was running before you installed the native modules, stop it now. After installing native dependencies, restart with `npx expo start -c` (the `-c` flag clears the bundler cache) or run `npx expo run:ios` / `npx expo run:android` for a fresh development build. This prevents "native module not available" errors from stale cache.

### Configure app.json plugins

Open `app.json` and add the required plugins to the `expo.plugins` array:

```json
{
  "expo": {
    "plugins": ["expo-router", "expo-secure-store", "@clerk/expo"]
  }
}
```

The `@clerk/expo` plugin integrates the native Clerk SDKs (`clerk-ios` and `clerk-android`) during the build process. It defaults to enabling the Apple Sign-In entitlement (`appleSignIn: true`). If you do not need Apple Sign-In, disable it explicitly:

```json
{
  "expo": {
    "plugins": ["expo-router", "expo-secure-store", ["@clerk/expo", { "appleSignIn": false }]]
  }
}
```

Run your first development build to verify everything compiles:

```bash
npx expo run:ios
```

Or for Android:

```bash
npx expo run:android
```

**Checkpoint:** You should now have a configured Expo project with all dependencies installed and a successful development build.

## Setting up ClerkProvider

Replace the contents of `src/app/_layout.tsx` with the following:

```tsx
import { ClerkProvider } from '@clerk/expo'
import { tokenCache } from '@clerk/expo/token-cache'
import { Slot } from 'expo-router'

const publishableKey = process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY!

if (!publishableKey) {
  throw new Error('Add EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY to your .env file')
}

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

Here is what each part does:

- **`publishableKey`** — reads the `EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY` value from the `.env` file you created earlier. The `process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY` reference works because Metro inlines environment variables with this prefix at build time. This is required in Core 3 — the `@clerk/expo` SDK does not auto-detect environment variables.
- **`tokenCache`** — imported from `@clerk/expo/token-cache`, this uses `expo-secure-store` under the hood. On iOS, tokens are stored in the Keychain (encrypted by Secure Enclave). On Android, tokens are stored in SharedPreferences encrypted with the Android Keystore. Sessions persist across app restarts.
- **`Slot`** — an Expo Router component that renders the current route's content. This is the standard pattern for layout files.

Session tokens have a 60-second lifetime and are [proactively refreshed](https://clerk.com/docs/guides/how-clerk-works/overview.md) every 50 seconds, so the user's authentication state stays current without any manual token management.

**Checkpoint:** ClerkProvider wraps the entire app. The app should still compile and run without errors.

## Adding a starter home screen

You deleted the template's `index.tsx`, so the app has no route at `/` yet — Expo Router would show its "unmatched route" screen on launch. Create a minimal `src/app/index.tsx` to serve as the app's entry point:

```tsx
import { Link } from 'expo-router'
import { View, Text, StyleSheet } from 'react-native'

export default function HomeScreen() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Welcome to Clerk + Expo</Text>
      <Link href="/(auth)/sign-in" style={styles.link}>
        <Text style={styles.linkText}>Sign In</Text>
      </Link>
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
  },
  link: {
    padding: 12,
  },
  linkText: {
    fontSize: 16,
    color: '#6C47FF',
    fontWeight: '600',
  },
})
```

This gives you a public landing screen and a way to reach the sign-in route you build next. In Part 2 you will replace it with a version that renders different content for signed-in and signed-out users.

## Building the authentication screen with AuthView

### Create the auth route group

Expo Router uses route groups — directories wrapped in parentheses like `(auth)` — to organize routes without affecting the URL structure. Create the auth layout at `src/app/(auth)/_layout.tsx`:

```tsx
import { useAuth } from '@clerk/expo'
import { Redirect, Slot } from 'expo-router'

export default function AuthLayout() {
  const { isSignedIn, isLoaded } = useAuth()

  if (!isLoaded) {
    return null
  }

  if (isSignedIn) {
    return <Redirect href="/" />
  }

  return <Slot />
}
```

This layout checks the user's authentication state before rendering any auth screens:

- **`isLoaded`** — prevents premature redirects while Clerk's auth state initializes. Without this check, the layout might redirect before knowing whether the user is signed in.
- **`isSignedIn`** — if the user is already authenticated, the `<Redirect>` component navigates them to the home screen. This prevents signed-in users from seeing the sign-in screen.
- **`<Slot />`** — renders child routes (in this case, `sign-in.tsx`) when the user is not signed in.

> This tutorial uses the `<Redirect>` component from `expo-router` rather than imperative `router.replace()` inside render. `<Redirect>` integrates correctly with the navigation stack and avoids flash or back-stack issues. Clerk's minimal quickstart uses a simpler single-screen approach without route groups, but route groups scale better for production apps with multiple screens.

### Add the sign-in screen with AuthView

Create `src/app/(auth)/sign-in.tsx` — this is the core of the tutorial:

```tsx
import { useAuth } from '@clerk/expo'
import { AuthView } from '@clerk/expo/native'
import { useRouter } from 'expo-router'
import { useEffect } from 'react'
import { View, StyleSheet } from 'react-native'

export default function SignInScreen() {
  const { isSignedIn } = useAuth({ treatPendingAsSignedOut: false })
  const router = useRouter()

  useEffect(() => {
    if (isSignedIn) {
      router.replace('/')
    }
  }, [isSignedIn])

  return (
    <View style={styles.container}>
      <AuthView />
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
})
```

Here is a line-by-line breakdown:

- **`AuthView`** is imported from `@clerk/expo/native` — this is the native component entry point, separate from web components at `@clerk/expo/web`.
- **`useAuth` with pending state disabled** — passing `{ treatPendingAsSignedOut: false }` is critical. When a user authenticates through AuthView, the native SDK creates a session before the JavaScript SDK knows about it. There is a brief "pending" period during synchronization. With the default `{ treatPendingAsSignedOut: true }`, `isSignedIn` flashes `false` during this sync, causing redirect loops. Setting it to `false` treats the pending state as signed-in, allowing the sync to complete.
- **`useEffect`** watches `isSignedIn` and redirects to the home screen when authentication completes.
- **`<AuthView />`** fills its parent container. The `flex: 1` style ensures it takes up the full screen.

The session synchronization flow works as follows:

1. The user interacts with the native AuthView UI
2. The native SDK (`clerk-ios` or `clerk-android`) creates a session
3. `@clerk/expo` syncs the native session to the JavaScript SDK
4. React hooks (`useAuth`, `useUser`) update automatically
5. The `useEffect` detects `isSignedIn === true` and triggers navigation

### Understanding AuthView props

AuthView's prop list stays short — `mode`, `isDismissible`, `onDismiss`, `logo`, and `logoMaxHeight` — because authentication methods are configured in the [Clerk Dashboard](https://dashboard.clerk.com/~/user-authentication/user-and-authentication), not in code.

The `mode` prop controls which flows are displayed:

```tsx
<AuthView />
```

This is equivalent to setting `mode="signInOrUp"`.

To restrict to sign-in only:

```tsx
<AuthView mode="signIn" />
```

To restrict to sign-up only:

```tsx
<AuthView mode="signUp" />
```

The `isDismissible` prop controls whether the user can dismiss the authentication screen. It defaults to `true`, which shows a dismiss button in the navigation bar. Set it to `false` when authentication is required and the user must complete it before continuing:

```tsx
<AuthView isDismissible={false} />
```

Pair `isDismissible` with the `onDismiss` callback to run navigation or cleanup logic when the user closes a dismissible view.

**Checkpoint:** Run the app, tap **Sign In** on the home screen, and you should see the native sign-in/sign-up interface. Create a test account to verify authentication completes — you land back on the home screen, which confirms the redirect fired.

## Conclusion

You have successfully set up your Clerk application, configured your Expo project with the necessary native dependencies, and built a native authentication screen using AuthView. The app is now capable of handling sign-in and sign-up flows securely using platform-native UI. In [Part 2](https://clerk.com/articles/how-to-use-authview-in-an-expo-app-native-authentication-with-clerk-2.md), you will turn the starter home screen into a state-aware one, add a protected profile route with user profile management, and learn how to handle authentication state and navigation across your app.

## Frequently asked questions

## FAQ

### What Expo SDK version is required for AuthView?

Current `@clerk/expo` releases require Expo SDK 54 or later (the package supports `expo >=54 <58`). Native components first shipped with SDK 53 support in `@clerk/expo` 3.1. This tutorial targets SDK 55.

### Does AuthView work with the React Native CLI (without Expo)?

It needs the `@clerk/expo` package and its config plugin, so it works in any Expo project that supports development builds, including the bare workflow (`expo` installed plus `expo prebuild`). A pure React Native CLI project with no Expo tooling cannot use these native components.

## In this series

1. **How to Use AuthView in an Expo App: Native Authentication with Clerk** (you are here)
2. [How to Use AuthView in an Expo App: Native Authentication with Clerk - Part 2](https://clerk.com/articles/how-to-use-authview-in-an-expo-app-native-authentication-with-clerk-2.md)
