# Develop

## Tools for development

An overview of Expo tools and websites that will help you during various aspects of your project-building journey.

When you create a new project with Expo, learning about the following essential tools and websites can help you during your app development journey. This page provides an overview of a list of recommended tools.
## Expo CLI
Expo CLI is a development tool and is installed automatically with `expo` package when you create a new project. You can use it by leveraging `npx` (a Node.js package runner).
It is designed to help you move faster during the app development phase. For example, your first interaction with Expo CLI is starting the development server by running the command: `npx expo start`.
The following is a list of common commands that you will use with Expo CLI while developing your app:
| Command                         | Description                                                                                                                                                      |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `npx expo start`                | Starts the development server (whether you are using a development build or Expo Go).                                                                            |
| `npx expo prebuild`             | Generates native Android and iOS directories using [Prebuild](/workflow/prebuild/).                                                                              |
| `npx expo run:android`          | Compiles native Android app locally.                                                                                                                             |
| `npx expo run:ios`              | Compiles native iOS app locally.                                                                                                                                 |
| `npx expo install package-name` | Used to install a new library or validate and update specific libraries in your project by adding `--fix` option to this command.                                |
| `npx expo lint`                 | [Setup and configures](/guides/using-eslint/) ESLint. If ESLint is already configured, this command will [lint your project files](/guides/using-eslint/#usage). |
In a nutshell, Expo CLI allows you to develop, compile, start your app, and more. See [Expo CLI reference](/more/expo-cli/) for more available options and actions you can perform with the CLI.
## EAS CLI
EAS CLI is used to log in to your Expo account and compile your app using different EAS services such as Build, Update, or Submit. You can also use this tool to:
- Publish your app to the app stores
- Create a development, preview, or production build of your app
- Create over-the-air (OTA) updates
- Manage your app credentials
- Create an ad hoc provisioning profile for an iOS device
To use EAS CLI, you need to install it globally on your local machine by running the command:
```sh
$ npm install -g eas-cli
```
You can use `eas --help` in your terminal window to learn more about the available commands. For a complete reference, see [`eas-cli` npm page](https://www.npmjs.com/package/eas-cli).
## Expo Doctor
Expo Doctor is a command line tool used to diagnose issues in your Expo project. To use it, run the following command in your project's root directory:
```sh
$ npx expo-doctor
```
This command performs checks and analyzes your project's codebase for common issues in [app config](/workflow/configuration/) and **package.json** files, dependency compatibility, configuration files, and the overall health of the project. Once the check is complete, Expo Doctor outputs the results.
If Expo Doctor finds an issue, it provides a description of the problem along with advice on how to fix it or where to find help.
By default, Expo Doctor validates your project's packages against the [React Native directory](https://reactnative.directory/) and checks if app config properties are properly synced when native directories exist. You can configure these checks in your project's **package.json** file. See [`reactNativeDirectoryCheck`](/versions/latest/config/package-json/#reactnativedirectorycheck) and [`appConfigFieldsNotSyncedCheck`](/versions/latest/config/package-json/#appconfigfieldsnotsynced) for more details.
You can also use `npx expo-doctor --help` to display usage information.
## Orbit
Orbit is a macOS and Windows app that enables:
- Install and launch builds from EAS on physical devices and emulators.
- Install and launch updates from EAS on Android Emulators or iOS Simulators.
- Launch snack projects on Android Emulators or iOS Simulators.
- Use local files to install and launch apps. Orbit supports any Android **.apk**, iOS Simulator compatible **.app**, or ad hoc signed apps.
- See a list of pinned projects from your EAS dashboard.
### Installation
For macOS: 
You can download Orbit with Homebrew for macOS, or directly from the [GitHub releases](https://github.com/expo/orbit/releases).
```sh
$ brew install expo-orbit
```
If you want Orbit to start when you log in automatically, click on the Orbit icon in the menu bar, then **Settings** and select the **Launch on Login** option.
For Windows: 
> **important** Orbit for Windows is in beta and is only compatible with x64 and x86 machines. Compatibility for other architectures will be added in the future.
You can download Orbit for Windows directly from the [GitHub releases](https://github.com/expo/orbit/releases).
> **info** Orbit relies on the Android SDK on both macOS and Windows and `xcrun` for device management only on macOS, which requires setting up both [Android Studio](/workflow/android-studio-emulator/) and [Xcode](/workflow/ios-simulator/).
{/* ### Usage */}
## Expo Tools for VS Code
Expo Tools is a VS Code extension to improve your development experience when working with app config files. It provides features such as autocomplete and intellisense for files such as app config, EAS config, store config and Expo Module config files.
You can also use it to debug your app using VS Code's built-in debugger to set breakpoints, inspect variables, execute code through the debug console, and more. See [Debugging with VS Code](/debugging/tools/#debugging-with-vs-code) for how to use this extension for debugging.
## Test prototypes with Snack and Expo Go
### Snack
Snack is an in-browser development environment that works similarly to Expo Go. It's a great way to share code snippets and experiment with React Native without downloading any tools on your computer.
To use it, go to [snack.expo.dev](https://snack.expo.dev/), edit the `<Text>` component in **App.js**, choose a platform (Android, iOS, or web) in the right panel and see the changes live.
### Expo Go
[Expo Go](https://expo.dev/go) is a free, open-source playground for students and learners to try out React Native. It works with Android and iOS.
For more information on how to use it:
- Click [this link](/get-started/set-up-your-environment/?mode=expo-go) to go to Set up your environment guide
- Select a platform to develop under **Where would you like to develop?**
- Select Expo Go under **How would you like to develop?**
- Follow the instructions described in that guide
> **Note:** Expo Go is limited and not useful for building production-grade projects. Use [development builds](/get-started/set-up-your-environment/?mode=development-build) instead.
Note: What if I open a project with an unsupported SDK version?
---
When running a project that was created for an unsupported SDK version in Expo Go, you'll see the following error:
```sh
"Project is incompatible with this version of Expo Go"
```
To fix this, upgrading your project to a [supported SDK version](/versions/latest/#each-expo-sdk-version-depends-on-a-react-native-version) is recommended. If you want to learn how to do it, see [Upgrade the project to a new SDK Version](#how-do-i-upgrade-my-project-from).
---
Note: How do I upgrade my project from an unsupported SDK version?
---
See [Upgrading Expo SDK guide](/workflow/upgrading-expo-sdk-walkthrough) for instructions for upgrading to a specific SDK version.
---
## React Native directory
Any library that is compatible with React Native works in an Expo project when you use a development build to create your project.
[reactnative.directory](https://reactnative.directory/) is a searchable database for React Native libraries. If a library you are looking for is not included in Expo SDK, use the directory to find a compatible library for your project.


## Navigation in Expo and React Native apps

Learn about the recommended approach for integrating navigation in an Expo and React Native project.

The core React Native library does not include a built-in navigation solution, so you can choose a navigation library that best fits your needs. For Expo and React Native apps, it is generally a choice between [React Navigation](https://reactnavigation.org/) or [Expo Router](/router/introduction/).
## Why React Native apps needs a navigation library
React Native core includes basic UI components, touch handling, device APIs and networking, but excludes, among other things, storage, camera, maps, most device sensors, and **navigation**! These are intended to be covered by community libraries.
## React Navigation
React Navigation is a component-based navigation library widely used across the React Native ecosystem. It lets you compose stack, tab, and drawer navigators entirely in code so you can implement complex flows, custom transitions, and app-specific UX patterns.
The library offers platform-specific look-and-feel with smooth animations and gestures, unified mobile and web routing, automatic deep links, type routes with static configuration, and is highly customizable.
## Expo Router (recommended for Expo projects)
Expo Router is a file-based routing library for Expo and React Native projects and is a built on top of React Navigation. By following the **app** directory convention, it turns files into routes and is integrated with Expo for [Expo CLI](/more/expo-cli/) and bundling without additional setup. The library also adds features such as typed routes, dynamic routes, lazy bundling in development, static rendering for the web, and automatic deep linking.
New Expo projects created with `npx create-expo-app@latest` include Expo Router by default so you can ship cross-platform navigation quickly while still being able to reach for React Navigation APIs when needed.


## Authentication in Expo and React Native apps

Learn about setting up authentication in your Expo project.

Authentication is a critical part of 90 to 95 percent of modern apps. This guide explains common methods, patterns, and solutions to help you implement authentication in your Expo app.
> **info** **TL;DR**: Auth is hard. If you want to skip the complexity, jump to the [Auth solutions](/#auth-solutions) section for ready-made solutions. Otherwise, keep reading.
Implementing authentication involves more than writing client-side code. You'll need to manage server requests, password flows, third-party providers like Google or Apple, email handling, and OAuth standards. It can get complex quickly.
There are several types of authentication methods. Some are simple and effective, while others offer a better user experience but require more work. Let's look at the most common approaches and how you can implement them.
## Navigation auth flow
Let's start with the basics: any authentication system needs to separate **public screens** (such as login or signup) from **protected screens** (such as home or profile). At the navigation level, it comes down to a simple check: is the user authenticated?
To begin, you can simulate this using a hardcoded boolean value, like `isAuthenticated = true`, and build your navigation logic around it. Once everything is working, you can plug in your real authentication flow.
Note: Using Expo Router
---
Expo Router v5 introduced [protected routes](/router/advanced/protected), which prevent users from accessing certain screens unless they are authenticated. This feature works well for client-side navigation and simplifies your setup.
If you're using an older version of Expo Router, you can use [redirects](/router/advanced/authentication-rewrites) instead. Redirects provide the same result but require a bit more manual configuration. They are still supported in Expo Router v5 for backward compatibility.
Video Tutorial: [Expo Router Protected Routes](https://www.youtube.com/watch?v=zHZjJDTTHJg)
---
Note: Using React Navigation
---
If you're using React Navigation, they offer a helpful [authentication flow guide](https://reactnavigation.org/docs/auth-flow/) that explains how to structure your navigation logic. It includes examples for both [static](https://reactnavigation.org/docs/auth-flow/?config=static#how-it-will-work) and [dynamic](https://reactnavigation.org/docs/auth-flow/?config=dynamic#how-it-will-work) approaches based on the user's authentication state.
---
Both Expo Router and React Navigation give you flexible tools to implement protected navigation based on whether the user is logged in.
## Email and password
Email and password is a popular option when adding authentication to your app.
To make this flow user-friendly, you also need to implement forgot password and reset password functionality so users who lose access to their accounts can recover them.
If you want a quicker solution, several services offer built-in email and password authentication, including [Clerk](/#clerk), [Supabase](/#supabase), [Cognito](/#cognito), [Firebase](/#firebase-auth), and [Better Auth](/#better-auth). Most of these have generous free tiers, but it is a good idea to evaluate pricing if your app grows quickly.
The biggest advantage of these services is their ease of integration. They usually offer clear documentation, starter kits, and prebuilt components that save you time.
Note: Security checklist (OWASP) and store-review gotchas
---
If you're building this flow yourself, be sure to review the [Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html#authentication-cheat-sheet) by OWASP. It outlines best practices for password length, encryption, recovery, secure storage, and more.
> **info** Adding email and password authentication is usually enough to pass App Store and Play Store review. You can submit your app with this method first. If you include "Sign in with Google," Apple may reject your app unless you also support "Sign in with Apple." The same rule applies in reverse on Google Play.
---
## Passwordless login
Passwordless login removes the need for users to create or remember a password. Instead, they provide their email address or phone number during registration. Your app then sends a [magic link](https://auth0.com/docs/authenticate/passwordless/authentication-methods/email-magic-link#classic-login-flow-with-magic-links) or [one-time passcode (OTP)](https://en.wikipedia.org/wiki/One-time_password) to their inbox or device. This is a smoother experience for most users and reduces friction during onboarding.
Note: Magic Links
---
With magic links, the user receives an email containing a link that redirects them back into your app. If everything works correctly, the session is verified and established.
A key detail here is [deep linking](/linking/into-your-app). Since users leave the app to check their email, the link must open your app and route them to the correct screen. If deep linking fails, the session cannot be validated, and the login flow breaks.
If you're using Expo Router deep linking is handled automatically (for most cases). You usually don't need to configure anything extra to make magic links work properly, which makes this approach even easier to adopt. See [Linking into your app](/linking/into-your-app) to learn more.
[React Navigation](https://reactnavigation.org/) also supports deep linking, but you will need to configure it manually. See its [Deep Linking guide](https://reactnavigation.org/docs/deep-linking/) for more details.
---
Note: One-Time Passcodes (OTP)
---
An alternative to magic links is sending a one-time passcode by email or SMS. Instead of clicking a link, the user copies the code and manually returns to the app to enter it. This must happen within a specific time window before the code expires.
There's no deep linking involved here. The user stays in control of the flow and must return to the app themselves.
Fortunately, newer versions of Android and iOS automatically detect passcodes in incoming messages. This enables autofill suggestions above the keyboard, allowing users to enter the code with a single tap. When this works, the experience is seamless.
---
> **info** Magic links and passcodes are both valid authentication methods for Google Play Store and Apple App Store reviews. You can submit your app with either of these methods as the only option and get approved, even before adding social or OAuth login options.
## OAuth 2.0
To let your users log in using their existing accounts from services like Google, Apple, GitHub, and more, you can use OAuth 2.0.
[OAuth 2.0](https://oauth.net/2) is a widely used, secure protocol that allows your app to access user information from another service, without needing to handle passwords. It allows your users to log in with a single tap, which saves time, builds trust, and removes the need to manage passwords.
> **info** OAuth flows can be complex. If you're looking for a simple integration, most providers offer SDKs and services that handle everything for you. You can learn more about these in the [Auth solutions](#auth-solutions) section.
If you are looking for full control or want to understand how OAuth works under the hood, the following sections show how to implement a complete OAuth flow yourself using Expo.
### How OAuth works
OAuth works by introducing an authorization server that acts as a secure middleman. Instead of giving your app their password, users log in through this server and approve access to specific data (like their name or email). The server then issues a temporary code, which your app can exchange for a secure access token.
Once you understand this pattern, you can apply it to any provider. The setup for Google, Apple, or GitHub will follow the same general steps.
### Custom OAuth with Expo API Routes
The previous diagram shows a high-level overview of the OAuth flow. However, the preferred method for a client to obtain an authorization grant from the user is to use an authorization server as an intermediary, which is exactly what you can build using Expo API Routes.
The following diagram illustrates this flow in more detail:
Expo lets you implement the entire OAuth flow directly in your app using:
Some providers offer native APIs to handle the sign-in flow directly within the app. Google offers a native Sign in with Google experience on Android. If you're looking for a native implementation, see the [Google authentication guide](/guides/google-authentication). Apple provides Sign in with Apple, which uses a native bottom sheet and Face ID on iOS. See [`expo-apple-authentication`](/versions/latest/sdk/apple-authentication) reference.
The following setup gives you full control over the login experience across Android, iOS, and web.
Note: What are Expo API Routes?
---
[Expo Router API Routes](/router/web/api-routes) allow you to write server-side logic directly inside your Expo app. You can define functions that handle requests just like an Express or Next.js backend, no need for an external server.
This makes it easy to securely handle sensitive parts of the auth flow, like the [authorization code exchange](https://www.oauth.com/oauth2-servers/pkce/authorization-code-exchange), directly within your app. Since these routes run on the server, you can safely manage secrets, issue JWTs, and validate tokens.
> **info** You're essentially building a lightweight custom auth server scoped to your own application, all using your Expo project.
---
Note: What is Expo AuthSession?
---
[Expo AuthSession](/versions/latest/sdk/auth-session) is a client-side package that helps you open a web browser or native modal to start the OAuth login flow. It handles redirection, parses the authorization response, and brings the user back into your app.
It's the tool that kicks off the flow and talks to your API Route after the user authorizes access. See [Authentication with OAuth or OpenID providers](/guides/authentication/) for more information.
---
This setup lets you:
- Start the login flow using AuthSession
- Receive the auth code in your API Route
- Exchange the code for a token securely
- Generate a custom JWT with your own logic
- Return that token to the client
- Store sessions using cookies (Web) or JWTs (Native)
- Deploy instantly using EAS Hosting (free to start)
The following tutorials cover implementing OAuth on Android, iOS, and web, including how to create and verify custom JWTs, manage sessions, and protect API routes. If you're new to this flow, we recommend starting with the Google tutorial.
Video Tutorial: [Google Sign-In with Expo OAuth](https://www.youtube.com/watch?v=V2YdhR1hVNw)
Video Tutorial: [Sign in with Apple using Expo](https://www.youtube.com/watch?v=tqxTijhYhp8)
Note: Managing sessions after OAuth
---
Handling the OAuth flow securely is just the beginning. Once the user is authenticated, you need to think about how to store, restore, and validate their session.
This includes:
- Storing the session securely on the client
- Restoring it when the app restarts
- Protecting your API routes so only authenticated users can access them
Traditionally, [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies#what_cookies_are_used_for) are used to store sessions on the web, while [JSON Web Tokens (JWTs)](https://en.wikipedia.org/wiki/JSON_Web_Token) are common in native applications.
The above tutorials demonstrate exactly how to handle this. After receiving the ID token from a provider like Google or Apple, you generate a custom JWT on the server using Expo API Routes.
This gives you full control over the session, including:
- Structuring the payload using consistent fields across providers
- Customizing expiration times
- Signing the token with a secret key so your server can verify it later
Once the token is created:
- For Android and iOS apps, you can store it securely using [`expo-secure-store`](/versions/latest/sdk/securestore/)
- For web apps, you can set it as a secure cookie to maintain the session
On every request, the token is sent back to your server, where you verify the signature and check the expiration. If everything checks out, you continue processing the request.
This session model keeps your backend stateless, scalable, and secure, and works consistently across platforms.
All of this is covered in the video tutorials linked above, including:
- Generating and verifying custom JWTs
- Handling session storage with Secure Store and cookies
- Protecting API routes with authentication logic
---
## Auth solutions
If you prefer not to build a full authentication system from scratch, several services offer built-in solutions with first-class support for Expo. Here are some of the most popular options:
Note: Better Auth
---
[BetterAuth](https://www.better-auth.com/docs/integrations/expo) is a modern, open-source authentication provider built for developers. It integrates smoothly with Expo, and they offer a guide that shows how to use it with [Expo API Routes](https://www.better-auth.com/docs/integrations/expo) for full control. It works well with any provider and deploys easily with EAS Hosting.
---
Note: Clerk
---
[Clerk](https://clerk.com/expo-authentication) is a powerful, full-featured authentication service with excellent Expo support. It includes email/password, passcodes, magic links, OAuth providers, and even passkeys. They also offer a native Expo module that handles much of the integration for you.
---
Note: Supabase
---
[Supabase](https://supabase.com/docs/guides/getting-started/tutorials/with-expo-react-native) provides a full backend platform, including a built-in authentication service that works with any OAuth provider. It integrates well with Expo apps and also includes support for email, magic links, and more.
---
Note: Cognito
---
[AWS Cognito](https://medium.com/@juliuscecilia33/aws-cognito-and-react-native-bf23ef7fea23) is Amazon's solution for managing user pools and identity. It connects seamlessly with other AWS services and can be integrated into Expo apps using AWS Amplify. It does require more configuration, but it's robust and scalable.
---
Note: Firebase Auth
---
[Firebase Authentication](https://rnfirebase.io/auth/usage) is Google's auth platform and supports email, magic links, and OAuth providers. It works with React Native through [`react-native-firebase`](https://github.com/invertase/react-native-firebase), which is compatible with Expo development builds.
---
## Modern methods
Once you have a working authentication system in place, you can improve the user experience by adding optional but powerful enhancements like biometrics and passkeys. These features add convenience, trust, and speed to your login flows.
Note: Biometrics
---
Biometrics like Face ID and Touch ID can be used to unlock the app or confirm identity after a valid session is established. These are not authentication methods on their own, but act as a local gate that makes re-authentication faster and more secure.
React Native provides access to biometric APIs through libraries like [`expo-local-authentication`](/versions/latest/sdk/local-authentication) or [`react-native-biometrics`](https://github.com/SelfLender/react-native-biometrics).
---
Note: Passkeys
---
[Passkeys](https://safety.google/authentication/passkey) are a new, passwordless way to log in to apps and websites. Backed by Apple, Google, and Microsoft, they use platform-level cryptography and biometrics to authenticate users without passwords.
Passkeys offer a seamless and secure experience, but they require a user to already be authenticated before registering one. They also require extra configuration if you're not using a provider that handles them for you.
- React Native passkey support: [`react-native-passkeys`](https://github.com/peterferguson/react-native-passkeys)
- Native passkey support with Clerk: [Clerk Passkeys for Expo](https://clerk.com/docs/references/expo/passkeys)
---
## Recommendations
This guide covers a lot of ground, from basic email and password flows to fully custom OAuth implementations, session management, and modern methods like biometrics and passkeys. Not all of these need to be implemented at once.
In many cases, starting simple is the best approach. Shipping your app with something like email authentication using a magic link or one-time passcode is often more than enough to get through the App Store review process and start collecting feedback from real users.
That said, if you're building an app where you expect high traffic from day one or need to support sign-in across platforms with minimal friction, investing in a more complete authentication flow early on can make a big difference. It can help improve user onboarding, trust, and retention right from the start.
Modern solutions like OAuth, biometrics, and passkeys are not required, but they can be excellent additions once your core system is in place.
The key is to build authentication that fits your current needs, while staying flexible enough to grow with your product.


## Unit testing with Jest

Learn how to set up and configure the jest-expo library to write unit and snapshot tests for a project with Jest.

[Jest](https://jestjs.io) is the most widely used unit and snapshot JavaScript testing framework. In this guide, you will learn how to set up Jest in your project, write a unit test, write a snapshot test, and best practices for structuring your tests when using Jest with React Native.
You will also use the [`jest-expo`](https://github.com/expo/expo/tree/main/packages/jest-expo) library, which is a Jest preset that mocks the native part of the Expo SDK and handles most of the configuration required for your Expo project.
## Installation and configuration
After creating your Expo project, follow the instructions below to install and configure `jest-expo` in your project:
Step 1: 
Install `jest-expo` and other required dev dependencies in your project. Run the following command from your project's root directory:
For macOS/Linux: 
```sh
$ npx expo install jest-expo jest @types/jest --dev
```
For Windows: 
```sh
$ npx expo install jest-expo jest @types/jest "--" --dev
```
> **Note:** If your project is not using TypeScript, you can skip installing `@types/jest`.
Step 2: 
Open **package.json**, add a script for running tests, and add the preset for using the base configuration from `jest-expo`:
```json package.json
{
  "scripts": {
  },
  "jest": {
    "preset": "jest-expo"
  }
}
```
Step 3: 
In **package.json**, add `jest-expo` as a preset so that a base for Jest's configuration is set up:
```json package.json
{
  "jest": {
    "preset": "jest-expo"
  }
}
```
Note: Additional configuration for using 
---
You can transpile node modules your project uses by configuring [`transformIgnorePatterns`](https://jestjs.io/docs/configuration#transformignorepatterns-arraystring) in your **package.json**. This property takes a regex pattern as its value:
For npm/Yarn: 
```json package.json
"jest": {
  "preset": "jest-expo",
  "transformIgnorePatterns": [
    "node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg)"
  ]
}
```
For pnpm: 
```json package.json
"jest": {
  "preset": "jest-expo",
  "transformIgnorePatterns": [
    "node_modules/(?!(?:.pnpm/)?((jest-)?react-native|@react-native(-community)?|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg))"
  ]
}
```
Jest has many configuration options, but the above configuration should cover most of your needs. However, you can always add to this pattern list. For more details, see [Configuring Jest](https://jestjs.io/docs/configuration).
---
## Install React Native Testing Library
The [React Native Testing Library (`@testing-library/react-native`)](https://callstack.github.io/react-native-testing-library/) is a lightweight solution for testing React Native components. It provides utility functions and works with Jest.
To install it, run the following command:
For macOS/Linux: 
```sh
$ npx expo install @testing-library/react-native --dev
```
For Windows: 
```sh
$ npx expo install @testing-library/react-native "--" --dev
```
> **warning** **Deprecated:** `@testing-library/react-native` replaces the deprecated `react-test-renderer` because `react-test-renderer` does not support React 19 and above. Remove the deprecated library from your project if you are currently using it. See [React's documentation for more information](https://react.dev/warnings/react-test-renderer).
## Unit test
A unit test checks the smallest unit of code, usually a function. To write your first unit test, take a look at the following example:
Step 1: 
Inside the **app** directory of your project, create a new file called **index.tsx**, and the following code to render a simple component:
```tsx index.tsx
import { PropsWithChildren } from 'react';
import { StyleSheet, Text, View } from 'react-native';
export const CustomText = ({ children }: PropsWithChildren) => <Text>{children}</Text>;
export default function HomeScreen() {
  return (
    <View style={styles.container}>
      <CustomText>Welcome!</CustomText>
    </View>
  );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});
```
Step 2: 
Create a **\_\_tests\_\_** directory at the root of your project's directory. If this directory already exists in your project, use that. Then, create a new file called **HomeScreen-test.tsx**. The `jest-expo` preset customizes the Jest configuration to also identify files with **-test.ts|tsx** extensions as tests.
Add the following example code in **HomeScreen-test.tsx**:
```tsx HomeScreen-test.tsx
import { render } from '@testing-library/react-native';
import HomeScreen, { CustomText } from '@/app/index';
describe('<HomeScreen />', () => {
  test('Text renders correctly on HomeScreen', () => {
    const { getByText } = render(<HomeScreen />);
    getByText('Welcome!');
  });
});
```
In the above example, the `getByText` query helps your tests find relevant element in your app's user interface and make assertion whether or not the certain element exists. The React Native Testing Library provides this query, and each [query variant](https://callstack.github.io/react-native-testing-library/docs/api/queries#query-variant) differs in its return type. For more examples and detailed API information, see the React Native Testing Library's [Queries API reference](https://callstack.github.io/react-native-testing-library/docs/api/queries).
Step 3: 
Run the following command in a terminal window to execute the test:
```sh
$ npm run test
```
You will see one test being passed.
## Structure your tests
Organizing your test files is important to make them easier to maintain. A common pattern is creating a **\_\_tests\_\_** directory and putting all your tests inside.
An example structure of tests next to the **components** directory is shown below:
```
âââ __tests__/
â   âââ ThemedText-test.tsx
âââ components/
    âââ ThemedText.tsx
    âââ ThemedView.tsx
```
Alternatively, you can have multiple **\_\_tests\_\_** sub-directories for different areas of your project. For example, create a separate test directory for **components**, and so on:
```
âââ components/
â   âââ ThemedText.tsx
â   âââ __tests__/
â       âââ ThemedText-test.tsx
âââ utils/
    âââ index.tsx
    âââ __tests__/
        âââ index-test.tsx
```
It's all about preferences, and it is up to you to decide how you want to organize your project directory.
## Snapshot test
> **info** **Note:** For UI testing, we recommend end-to-end tests instead of snapshot unit tests. See the [E2E tests with Maestro](/eas/workflows/examples/e2e-tests/) guide.
A [snapshot test](https://jestjs.io/docs/en/snapshot-testing) is used to make sure that UI stays consistent, especially when a project is working with global styles that are potentially shared across components.
To add a snapshot test for `<HomeScreen />`, add the following code snippet in the `describe()` in **HomeScreen-test.tsx**:
```tsx HomeScreen-test.tsx
describe('<HomeScreen />', () => {
  test('CustomText renders correctly', () => {
    const tree = render(<CustomText>Some text</CustomText>).toJSON();
    expect(tree).toMatchSnapshot();
  });
});
```
Run `npm run test` command, and you will see a snapshot created inside **\_\_tests\_\_\\\_\_snapshots\_\_** directory, and two tests passed.
## Code coverage reports
Code coverage reports can help you understand how much of your code is tested. To see the code coverage report in your project using the HTML format, in **package.json**, under `jest`, set the `collectCoverage` to true and use `collectCoverageFrom` to specify a list of files to ignore when collecting the coverage.
```json package.json
"jest": {
  ...
  "collectCoverage": true,
  "collectCoverageFrom": [
    "**/*.{ts,tsx,js,jsx}",
    "!**/coverage/**",
    "!**/node_modules/**",
    "!**/babel.config.js",
    "!**/expo-env.d.ts",
    "!**/.expo/**"
  ]
}
```
Run `npm run test`. You will see a **coverage** directory created in your project. Find the **lcov-report/index.html** and open it in a browser to see the coverage report.
> Usually, we don't recommend uploading **index.html** file to git. Add `coverage/**/*` in the **.gitignore** file to prevent it from being tracked.
## Jest flows (optional)
You can also use different flows to run your tests. Below are a few example scripts that you can try:
```json package.json
"scripts": {
  "test": "jest --watch --coverage=false --changedSince=origin/main",
  "testDebug": "jest -o --watch --coverage=false",
  "testFinal": "jest",
  "updateSnapshots": "jest -u --coverage=false"
}
```
For more information, see [CLI Options](https://jestjs.io/docs/en/cli) in Jest documentation.
## Additional information