# User interface

## Splash screen and app icon

Learn how to add a splash screen and app icon to your Expo project.

A splash screen and an app icon are fundamental elements of a mobile app. They play an important role in the user experience and branding of the app. This guide provides steps on how to create and add them to your app.
Video Tutorial: [Create an App Icon and Splash Screen](https://www.youtube.com/watch?v=3Bsw8a1BJoQ)
---
## Splash screen
A splash screen, also known as a launch screen, is the first screen a user sees when they open your app. It stays visible while the app is loading. You can also control the behavior of when a splash screen disappears by using the native [SplashScreen API](/versions/latest/sdk/splash-screen).
The [`expo-splash-screen`](/versions/latest/sdk/splash-screen) has a built-in [config plugin](/config-plugins/introduction) that lets you configure properties such as the splash icon and background color.
> **warning** **Do not use Expo Go or a development build to test your splash screen**. Expo Go renders your app icon while the splash screen is visible, which can interfere with testing. Development builds include `expo-dev-client`, which has its own splash screen and may cause conflicts. **Instead, use a [preview build](/build/eas-json/#preview-builds) or a [production build](/build/eas-json/#production-builds)**.
Step 1: 
### Create a splash screen icon
To create a splash screen icon, you can use this [Figma template](https://www.figma.com/community/file/1466490409418563617). It provides a bare minimum design for an icon and splash images for Android and iOS.
**Recommended:**
- Use a 1024x1024 image.
- Use a **.png** file.
- Use a transparent background.
Step 2: 
### Export the splash icon as a .png
After creating a splash screen icon, export it as a **.png** and save it in the **assets/images** directory. By default, Expo uses **splash-icon.png** as the file name. If you decide to change the name of your splash screen file, make sure to use that in the next step.
> **Note:** **Currently, only .png images are supported** to use as a splash screen icon in an Expo project. If you use another image format, making a production build of your app will fail.
Step 3: 
### Configure the splash screen icon
Open the app config file, and under plugins, set the following properties:
```json app.json
{
  "expo": {
    "plugins": [
      [
        "expo-splash-screen",
        {
          "backgroundColor": "#232323",
          "image": "./assets/images/splash-icon.png",
          "dark": {
            "image": "./assets/images/splash-icon-dark.png",
            "backgroundColor": "#000000"
          },
          "imageWidth": 200
        }
      ]
    ]
  }
}
```
To test your new splash screen, build your app for [internal distribution](/tutorial/eas/internal-distribution-builds) or for production, see guides on [Android](/tutorial/eas/android-production-build/) and [iOS](/tutorial/eas/ios-production-build/).
Note: Configuring  properties separately for Android and iOS
---
[`expo-splash-screen`](/versions/latest/sdk/splash-screen) also supports `android` and `ios` properties for configuring the splash screen for a specific platform. See the following example:
```json app.json
{
  "expo": {
    "plugins": [
      [
        "expo-splash-screen",
        {
          "ios": {
            "backgroundColor": "#ffffff",
            "image": "./assets/images/splash-icon.png",
            "resizeMode": "cover"
          },
          "android": {
            "backgroundColor": "#0c7cff",
            "image": "./assets/images/splash-android-icon.png",
            "imageWidth": 150
          }
        }
      ]
    ]
  }
}
```
---
Note: Not using prebuild?
---
If your app does not use [Expo Prebuild](/workflow/prebuild) (formerly the _managed workflow_) to generate the native **android** and **ios** directories, then changes in the app config will have no effect. For more information, see [how you can customize the configuration manually](https://github.com/expo/expo/tree/main/packages/expo-splash-screen#-installation-in-bare-react-native-projects).
---
Note: Troubleshooting: New splash screen not appearing on iOS
---
For SDK versions below 52, in iOS development builds, launch screens can sometimes remain cached between builds, making it harder to test new images. Apple recommends clearing the _derived data_ directory before rebuilding, this can be done with Expo CLI by running:
```sh
$ npx expo run:ios --no-build-cache
```
See [Apple's guide on testing launch screens](https://developer.apple.com/documentation/technotes/tn3118-debugging-your-apps-launch-screen) for more information.
---
## App icon
An app's icon is what your app users see on their device's home screen and app stores. Android and iOS have different and strict requirements.
Step 1: 
### Create an app icon
To create an app icon, you can use this [Figma template](https://www.figma.com/community/file/1466490409418563617). It provides a bare minimum design for an icon and splash images for Android and iOS.
Step 2: 
### Export the icon image as a .png
After creating an app icon, export it as **.png** and save it in the **assets/images** directory. By default, Expo uses **icon.png** as the file name. If you decide to use a different file name, make sure to use that in the next step.
Step 3: 
### Add the icon in app config
Open the app config and add the local path as the value of [`icon`](/versions/latest/config/app/#icon) property to point it to your new app icon:
```json app.json
{
  "icon": "./assets/images/icon.png"
}
```
Note: Custom configuration tips for Android and iOS
---
#### Android
Further customization of the Android icon is possible using the [`android.adaptiveIcon`](/versions/latest/config/app/#adaptiveicon) property, which will override both of the previously mentioned settings.
The Android Adaptive Icon is formed from two separate layers &mdash; a foreground image and a background color or image. This allows the OS to mask the icon into different shapes and also supports visual effects. For Android 13 and later, the OS supports a themed app icon that uses a wallpaper and theme to determine the color set by the device's theme.
The design you provide should follow the [Android Adaptive Icon Guidelines](https://developer.android.com/develop/ui/views/launch/icon_design_adaptive) for launcher icons. You should also:
- Use **.png** files.
- Use the `android.adaptiveIcon.foregroundImage` property to specify the path to your foreground image.
- Use the `android.adaptiveIcon.monochromeImage` property to specify the path to your monochrome image.
- The default background color is white; to specify a different background color, use the `android.adaptiveIcon.backgroundColor` property. You can instead specify a background image using the `android.adaptiveIcon.backgroundImage` property. Make sure that it has the same dimensions as your foreground image.
You may also want to provide a separate icon for older Android devices that do not support Adaptive Icons. You can do so with the `android.icon` property. This single icon would be a combination of your foreground and background layers.
> See [Apple best practices](https://developer.apple.com/design/human-interface-guidelines/app-icons/#Best-practices) to ensure your icon looks professional, such as testing your icon on different wallpapers and avoiding text beside your product's wordmark. Provide an icon that's at least 512x512 pixels.
#### iOS
<VideoBoxLink
  videoId="RZ_QMym3adw"
  title="Icon Composer"
  description="Learn how to use the new Icon Composer to create app icons for an Expo project."
  className="mb-4"
/>
For iOS, your app's icon should follow the [Apple Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/app-icons/). You can use the [Icon Composer](https://developer.apple.com/icon-composer/) app to create your app icon. This will output a **.icon** directory that you can add to your project's **assets** directory. You can then provide the path to this directory in your app config. Adding support for dark mode is handled in Icon Composer, so you do not need to provide variants when using this approach.
> **info** **Note:** Providing an Icon Composer **.icon** directory via `ios.icon` is supported **in SDK 54** and later.
```json app.json
{
  "expo": {
    "ios": {
      "icon": "./assets/app.icon"
    }
  }
}
```
Alternatively, the previous approach of providing an image is still supported. You should:
- Use a **.png** file.
- 1024x1024 is a good size. If you have an Expo project created using `npx create-expo-app`, [EAS Build](/build/setup/) will generate the other sizes for you. In case of a bare React Native project, generate the icons on your own. The largest size EAS Build generates is 1024x1024.
- The icon must be exactly square. For example, a 1023x1024 icon is not valid.
- Make sure the icon fills the whole square, with no rounded corners or other transparent pixels. The operating system will mask your icon when appropriate.
- Use `ios.icon` to specify different icons for various system appearances (for example, dark and tinted) can be provided. If specified, this overrides the top-level icon key in the app config file. See the example below:
```json app.json
{
  "expo": {
    "ios": {
      "icon": {
        "dark": "./assets/images/ios-dark.png",
        "light": "./assets/images/ios-light.png",
        "tinted": "./assets/images/ios-tinted.png"
      }
    }
  }
}
```
---


## Safe areas

Learn how to add safe areas for screen components inside your Expo project.

Creating a safe area ensures your app screen's content is positioned correctly. This means it doesn't get overlapped by notches, status bars, home indicators, and other interface elements that are part of the device's physical hardware or are controlled by the operating system. When the content gets overlapped, it gets concealed by these interface elements.
Here's an example of an app screen's content getting concealed by the status bar on Android. On iOS, the same content is concealed by rounded corners, notch, and the status bar.
## Use `react-native-safe-area-context` library
[`react-native-safe-area-context`](https://github.com/AppAndFlow/react-native-safe-area-context) provides a flexible API for handling Android and iOS device's safe area insets. It also provides a `SafeAreaView` component that you can use instead of a [`<View>`](https://reactnative.dev/docs/view) to account for safe areas automatically in your screen components.
Using the library, the result of the previous example changes as it displays the content inside a safe area, as shown below:
### Installation
You can skip installing `react-native-safe-area-context` if you have created a project using [the default template](/get-started/create-a-project/). This library is installed as peer dependency for Expo Router library. Otherwise, install it by running the following command:
```sh
$ npx expo install react-native-safe-area-context
```
### Usage
You can directly use [`SafeAreaView`](https://appandflow.github.io/react-native-safe-area-context/api/safe-area-view) to wrap the content of your screen's component. It is a regular `<View>` with the safe area insets applied as extra padding or margin.
```tsx app/index.tsx
import { Text } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
export default function HomeScreen() {
  return (
    <SafeAreaView style={{ flex: 1 }}>
      <Text>Content is in safe area.</Text>
    </SafeAreaView>
  );
}
```
Note: Using a different Expo template and don't have Expo Router installed?
---
Import and add [`SafeAreaProvider`](https://appandflow.github.io/react-native-safe-area-context/api/safe-area-provider) to the root component file (such as **App.tsx**) before using `SafeAreaView` in your screen component.
```tsx App.tsx
import { SafeAreaProvider } from 'react-native-safe-area-context';
export default function App() {
  return (
    return <SafeAreaProvider>...</SafeAreaProvider>;
  );
}
```
---
## Alternate: `useSafeAreaInsets` hook
Alternate to `SafeAreaView`, you can use [`useSafeAreaInsets`](https://appandflow.github.io/react-native-safe-area-context/api/use-safe-area-insets) hook in your screen component. It provides direct access to the safe area insets, allowing you to apply padding for each edge of the `<View>` using an inset from this hook.
The example below uses the `useSafeAreaInsets` hook. It applies top padding to a `<View>` using `insets.top`.
```tsx app/index.tsx
import { Text, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
export default function HomeScreen() {
  const insets = useSafeAreaInsets();
  return (
    <View style={{ flex: 1, paddingTop: insets.top }}>
      <Text>Content is in safe area.</Text>
    </View>
  );
}
```
The hook provides the insets in the following object:
```ts
{
  top: number,
  right: number,
  bottom: number,
  left: number
}
```
## Additional information
### Minimal example
Below is a minimal working example that uses the `useSafeAreaInsets` hook to apply top padding to a view.
#### Using react-native-safe-area-context
```tsx collapseHeight=320
import { Text, View } from 'react-native';
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
function HomeScreen() {
  const insets = useSafeAreaInsets();
  return (
    <View style={{ flex: 1, paddingTop: insets.top }}>
      <Text style={{ fontSize: 28 }}>Content is in safe area.</Text>
    </View>
  );
}
export default function App() {
  return (
    <SafeAreaProvider>
      <HomeScreen />
    </SafeAreaProvider>
  );
}
```
### Usage with React Navigation
By default, React Navigation supports safe areas and uses `react-native-safe-area-context` as a peer dependency. For more information, see the [React Navigation documentation](https://reactnavigation.org/docs/handling-safe-area/).
### Usage with web
If you are targeting the web, set up `SafeAreaProvider` as described in the [usage section](#usage). If you are doing server-side rendering (SSR), see the [Web SSR section](https://appandflow.github.io/react-native-safe-area-context/optimizations#web-ssr) in the library's documentation.


## System bars

Learn how to handle and customize system bars for safe areas and edge-to-edge layout in your Expo project.

System bars are the UI elements at the edges of the screen that provide essential device information and navigation controls. Depending on the mobile OS, they include the status bar ([Android](https://developer.android.com/design/ui/mobile/guides/foundations/system-bars) and [iOS](https://developer.apple.com/design/human-interface-guidelines/status-bars)), caption bar ([Android](https://medium.com/androiddevelopers/insets-handling-tips-for-android-15s-edge-to-edge-enforcement-872774e8839b#:~:text=or%20SHORT_EDGES.-,Caption%20bars,-When%20your%20app) only), navigation bar ([Android](https://developer.android.com/design/ui/mobile/guides/foundations/system-bars#navigation-bar) and [iOS](https://developer.apple.com/design/human-interface-guidelines/navigation-bars)), and home indicator (iOS only).
These components are used to display device information such as battery level, time, notification alerts, and provide direct interaction with the device from anywhere in the device's interface. For example, an app user can pull down the status bar to access quick settings and notifications regardless of which app they're currently using.
System bars are fundamental to the mobile experience, and understanding how to work with them properly is important for creating your app.
## Handling overlaps using safe areas
Some of your app's content may draw behind the system bars. To handle this, you need to position your app's content correctly by avoiding the overlap and ensuring that the controls from the system bars are present.
The following guide walks you through how to use `SafeAreaView` or a hook to apply insets directly for each edge of the screen.
### Safe areas and edge-to-edge layout on Android
Before [edge-to-edge on Android](https://expo.dev/blog/edge-to-edge-display-now-streamlined-for-android), it was common to have a translucent status bar and navigation bar. With this approach, the content drawn behind these bars was already underneath them, and it was typically not necessary to factor in safe areas.
Now, [with edge-to-edge on Android](https://expo.dev/blog/edge-to-edge-display-now-streamlined-for-android), you will need to use safe areas to ensure that content does not overlap with system bars.
## Customizing system bars
System bars can be customized to match your app's design and provide better visibility in different scenarios. When using Expo, there are two libraries available for this: `expo-status-bar` and `expo-navigation-bar` (Android only).
### Status bar configuration
The status bar appears at the top of the screen on both Android and iOS. You can customize it using [`expo-status-bar`](/versions/latest/sdk/status-bar). It provides a `StatusBar` component that you can use to control the appearance of the status bar while your app is running using the [`style`](/versions/latest/sdk/status-bar/#style) property or the [`setStatusBarStyle`](/versions/latest/sdk/status-bar/#statusbarsetstatusbarstylestyle-animated) method:
```tsx app/_layout.tsx
import { StatusBar } from 'expo-status-bar';
export default function RootLayout() {
  <>
    {/* Use light text instead of dark text in the status bar to provide more contrast with a dark background. */}
    <StatusBar style="light" />
  </>;
}
```
> **Note:** In Expo default template, the `style` property is set to `auto`. It automatically picks the appropriate style depending on the color scheme (light or dark mode) currently used by your app.
To control the `StatusBar` visibility, you can set the [`hidden`](/versions/latest/sdk/status-bar/#hidden) property to `true` or use the [`setStatusBarHidden`](/versions/latest/sdk/status-bar/#statusbarsetstatusbarhiddenhidden-animation) method.
**With edge-to-edge enabled on Android, features from `expo-status-bar` that depend on an opaque status bar [are unavailable](https://developer.android.com/about/versions/15/behavior-changes-15#edge-to-edge)**. It's only possible to customize the style and visibility. Other properties will no-op and warn.
### Navigation bar configuration (Android only)
On Android devices, the Navigation Bar appears at the bottom of the screen. You can customize it using the [`expo-navigation-bar`](/versions/latest/sdk/navigation-bar) library. It provides a `NavigationBar` component that you can use to set the style of the navigation bar using the [`setStyle`](/versions/latest/sdk/navigation-bar/#navigationbarsetstylestyle) method:
```tsx app/_layout.tsx
import { Platform } from 'react-native';
import * as NavigationBar from 'expo-navigation-bar';
import { useEffect } from 'react';
useEffect(() => {
  if (Platform.OS === 'android') {
    // Set the navigation bar style
    NavigationBar.setStyle('dark');
  }
}, []);
```
To control the `NavigationBar` visibility, you can use the [`setVisibilityAsync`](/versions/latest/sdk/navigation-bar/#navigationbarsetvisibilityasyncvisibility) method.
**With edge-to-edge enabled on Android, features from `expo-navigation-bar` that depend on an opaque navigation bar [are unavailable](https://developer.android.com/about/versions/15/behavior-changes-15#edge-to-edge)**. It's only possible to customize the style and visibility. Other properties will no-op and warn.


## Fonts

Learn how to integrate custom fonts in your app using local files or Google Font packages

Android and iOS come with their own set of platform fonts. To provide a consistent user experience and enhance your app's branding, you can use custom fonts.
This guide covers different ways you can add and load a custom font into your project and also provides additional information related to fonts.
## Add a custom font
There are two ways you can add a custom font into your project:
- Add a font file into your local assets. For example, a font file in the **assets/fonts** directory.
- Install a Google Font package. For example, installing [`@expo-google-fonts/inter`](https://www.npmjs.com/package/@expo-google-fonts/inter) package.
### Supported font formats
Expo SDK officially supports OTF and TTF font formats across Android, iOS and web platforms. If your font is in another font format, you have to set up advanced configuration to support that format in your project.
### Variable fonts
Variable fonts, including variable font implementations in OTF and TTF, do not have support across all platforms. For full platform support, use static fonts. Alternatively, use a utility such as [fontTools](https://fonttools.readthedocs.io/en/latest/varLib/mutator.html) to extract the specific axis configuration you want to use from the variable font and save it as a separate font file.
### How to choose between OTF and TTF
If the font you're using has both OTF and TTF versions, prefer OTF. The **.otf** files are smaller than **.ttf** files. Sometimes, OTF also renders slightly better in certain contexts.
## Use a local font file
Copy the file into your project's **assets/fonts** directory.
> **info** **assets/fonts** directory path is a common convention in React Native apps to put font files. You can place these files elsewhere if you follow a custom convention.
Two ways to use the local font file in your project:
- Embed the font file with [`expo-font` config plugin](/versions/latest/sdk/font/#configuration-in-app-config) (Android and iOS only).
- Load the font file with [`useFonts`](/versions/latest/sdk/font/#usefontsmap) hook at runtime (Android, iOS, and web).
### With `expo-font` config plugin
The `expo-font` config plugin allows embedding one or more font files in your project's native code. It supports `ttf` and `otf` for both Android and iOS, and `woff` and `woff2` are supported on iOS only.
> **Note:** Config plugins only run on native platforms (Android and iOS). For web, use the [`useFonts` hook](#with-usefonts-hook) instead.
This is the recommended method for adding fonts to your app due to its benefits:
- Fonts are available immediately when the app starts on a device.
- No additional code required to load fonts in a project asynchronously when the app starts.
- Fonts are consistently available across all devices where the app is installed because they're bundled within the app.
However, this method also has some limitations:
- Doesn't work with Expo Go since this method requires [creating a development build](/develop/development-builds/create-a-build/).
To embed a font in a project, follow the steps below:
Step 1: 
After adding a custom font file in your project, install the `expo-font` library.
```sh
$ npx expo install expo-font
```
Step 2: 
Add the config plugin to your [app config](/versions/latest/config/app/#plugins) file. The configuration must contain the path to the font file using [`fonts`, `android` or `ios`](/versions/latest/sdk/font/#configurable-properties) properties which take an array of one or more font definitions. The path to each font file is relative to the project's root.
The example below showcases all valid ways a font can be specified: as an array of objects that specify `fontFamily` and other properties, or an array of paths to font files.
For Android, you can specify the `fontFamily`, `weight`, and optionally `style` (defaults to `"normal"`), which will embed the fonts as native [XML resources](https://developer.android.com/develop/ui/views/text-and-emoji/fonts-in-xml). If you provide only the font file paths in an array, the file name becomes the font family name on Android. iOS always extracts the font family name from the font file itself.
If you plan to refer to fonts using just the `fontFamily`, provide an array of font paths (see `FiraSans-MediumItalic.ttf` below) and follow our [recommendation for file naming](#how-to-determine-which-font-family-name-to-use).
If you want to refer to fonts using a combination of `fontFamily`, `weight`, and `style`, provide an array of objects (see `Inter` below).
```json app.json
{
  "expo": {
    "plugins": [
      [
        "expo-font",
        {
          "fonts": [
            "./assets/fonts/FiraSans-MediumItalic.ttf"
          ],
          "android": {
            "fonts": [
              {
                "fontFamily": "Inter",
                "fontDefinitions": [
                  {
                    "path": "./assets/fonts/Inter-BoldItalic.ttf",
                    "weight": 700,
                    "style": "italic"
                  },
                  {
                    "path": "./assets/fonts/Inter-Bold.ttf",
                    "weight": 700
                  }
                ]
              }
            ]
          },
          "ios": {
            "fonts": ["./assets/fonts/Inter-Bold.ttf", "./assets/fonts/Inter-BoldItalic.ttf"]
          }
        }
      ]
    ]
  }
}
```
Step 3: 
After embedding the font with the config plugin, create a [new development build](/develop/development-builds/create-a-build/) and install it on your device or Android Emulator or iOS Simulator.
You can use the font with `<Text>` by specifying the `fontFamily` style prop. The examples below correspond to the fonts defined in the configuration above.
```tsx
<Text style={{ fontFamily: 'Inter', fontWeight: '700' }}>Inter Bold</Text>
<Text style={{ fontFamily: 'Inter', fontWeight: '700', fontStyle: 'italic' }}>Inter Bold Italic</Text>
<Text style={{ fontFamily: 'FiraSans-MediumItalic' }}>Fira Sans Medium Italic</Text>
```
<ConfigReactNative title="Using this method in an existing React Native project?">
- **Android:** Copy font files to **android/app/src/main/assets/fonts**.
- **iOS:** See [Adding a Custom Font to Your App](https://developer.apple.com/documentation/uikit/text_display_and_fonts/adding_a_custom_font_to_your_app) in the Apple Developer documentation.
</ConfigReactNative>
#### How to determine which font family name to use
- If you provide fonts as an array of file paths (as described above), on Android, the file name (without the extension) becomes the font family name. On iOS, the font family name is read from the font file itself. We recommend naming the font file same as its [PostScript name](#what-is-postscript-name-of-a-font) so the font family name is consistent on both platforms.
- If you use the object syntax, provide the "Family Name". This can be found in the Font Book app on macOS, [fontdrop.info](https://fontdrop.info/) or other programs.
Note: What is PostScript name of a font file?
---
The **PostScript name** of a font file is a unique identifier assigned to the font that follows Adobe's PostScript standard. It is used by operating systems and apps to refer to the font. It is not a font's **display name**.
For example, Inter Black font file's PostScript name is `Inter-Black`.
_Screenshot from Font Book app on macOS._
---
### With `useFonts` hook
The `useFonts` hook from `expo-font` library allows loading the font file asynchronously. This hook keeps track of the loading state and loads the font when an app is initialized.
It works with all Expo SDK versions and with Expo Go. To load a font in a project using `useFonts` hook, follow the steps below:
Step 1: 
After adding a custom font file in your project, install the `expo-font` and `expo-splash-screen` libraries.
```sh
$ npx expo install expo-font expo-splash-screen
```
The [`expo-splash-screen`](/versions/latest/sdk/splash-screen/) library provides `SplashScreen` component that you can use to prevent rendering the app until the font is loaded and ready.
Step 2: 
Map the font file using the `useFonts` hook in a top level component such as the root layout (**app/layout.tsx**) file in your project:
```tsx app/_layout.tsx
import {useEffect} from 'react';
SplashScreen.preventAutoHideAsync();
export default function RootLayout() {
  const [loaded, error] = useFonts({
    'Inter-Black': require('./assets/fonts/Inter-Black.otf'),
  });
  useEffect(() => {
    if (loaded || error) {
      SplashScreen.hideAsync();
    }
  }, [loaded, error]);
  if (!loaded && !error) {
    return null;
  }
  return (
  )
}
```
Step 3: 
Use the font on the `<Text>` by using `fontFamily` style prop in a React component:
```tsx
<Text style={{ fontFamily: 'Inter-Black' }}>Inter Black</Text>
```
## Use Google Fonts
Expo has first-class support for all fonts listed in [Google Fonts](https://fonts.google.com/). They are available using [`@expo-google-fonts`](https://github.com/expo/google-fonts) library. With any of the font package from this library, you can quickly integrate that font and its variants.
Two ways to use a Google Font in your project:
- Embed the installed font with [`expo-font` config plugin](/versions/latest/sdk/font/#configuration-in-appjsonappconfigjs).
- Load the installed font with [`useFonts`](/versions/latest/sdk/font/#usefontsmap) hook at runtime asynchronously.
### With `expo-font` config plugin
> **Note:** Embedding a Google Font using `expo-font` config plugin has same benefits and limitations as embedding a custom font on your own. See [using a local font file with `expo-font` config plugin](#with-expo-font-config-plugin) for more information.
Step 1: 
Install the font package. For example, to use Inter Black font, install the [`@expo-google-fonts/inter`](https://www.npmjs.com/package/@expo-google-fonts/inter) package with the command below.
```sh
$ npx expo install expo-font @expo-google-fonts/inter
```
Step 2: 
Add the config plugin to your [app config](/versions/latest/config/app/#plugins) file. The configuration must contain the path to the font file using [`fonts`](/versions/latest/sdk/font/#configurable-properties) property which takes an array of one or more font files. The path to the font file is defined from the font package inside the `node_modules` directory. For example, if you have a font package named `@expo-google-fonts/inter`, then the name of the file is **Inter_900Black.ttf**.
```json app.json
{
  "plugins": [
    [
      "expo-font",
      {
        "fonts": ["node_modules/@expo-google-fonts/inter/900Black/Inter_900Black.ttf"]
      }
    ]
  ]
}
```
Step 3: 
After embedding the font with the config plugin, create a [new development build](/develop/development-builds/create-a-build/) and install it on your device or Android Emulator or iOS Simulator.
On Android, you can use the font file name. For example, `Inter_900Black`. On iOS, use the font and its weight name ([PostScript name](#what-is-postscript-name-of-a-font)). The example below demonstrates how to use [`Platform`](https://reactnative.dev/docs/platform-specific-code#platform-module) to select the correct font family name for each platform:
```tsx
import { Platform } from 'react-native';
// Inside a React component:
<Text
  style={{
    fontFamily: Platform.select({
      android: 'Inter_900Black',
      ios: 'Inter-Black',
    }),
  }}>
  Inter Black
</Text>
```
### With `useFonts` hook
> **Note:** Loading a Google Font using `useFonts` hook has same benefits and limitations as embedding a custom font on your own. See [using a local font file with `useFonts` hook](#with-usefonts-hook) for more information.
Each google Fonts package provides the `useFonts` hook to load the fonts asynchronously. This hook keeps track of the loading state and loads the font when an app is initialized. The font package also imports the font file so you don't have to explicitly import it.
Step 1: 
Install the Google Fonts package, `expo-font` and `expo-splash-screen` libraries.
```sh
$ npx expo install @expo-google-fonts/inter expo-font expo-splash-screen
```
The [`expo-splash-screen`](/versions/latest/sdk/splash-screen/) library provides `SplashScreen` component that you can use to prevent rendering the app until the font is loaded and ready.
Step 2: 
After installing the font package, map the font using the `useFonts` hook in a top level component such as the root layout (**app/layout.tsx**) file in your project:
```tsx app/_layout.tsx
// Rest of the import statements
import { Inter_900Black, useFonts } from '@expo-google-fonts/inter';
import * as SplashScreen from 'expo-splash-screen';
import {useEffect} from 'react';
SplashScreen.preventAutoHideAsync();
export default function RootLayout() {
  const [loaded, error] = useFonts({
    Inter_900Black,
  });
  useEffect(() => {
    if (loaded || error) {
      SplashScreen.hideAsync();
    }
  }, [loaded, error]);
  if (!loaded && !error) {
    return null;
  }
  return (
  )
}
```
Step 3: 
Use the font on the `<Text>` by using `fontFamily` style prop in a React component:
```tsx
<Text style={{ fontFamily: 'Inter_900Black' }}>Inter Black</Text>
```
## Additional information
### Minimal example
### Beyond OTF and TTF
If your font is in format other than OTF or TTF, you have to [customize the Metro bundler configuration to include it as an extra asset](/guides/customizing-metro#adding-more-file-extensions-to-assetexts) for it to work. In some cases, rendering a font format that a platform doesn't support may cause your app to crash.
For reference, the following table provides the list formats that work on each native platform:
| Format | Android     | iOS         | Web         |
| ------ | ----------- | ----------- | ----------- |
| bdf    | <NoIcon />  | <NoIcon />  | <NoIcon />  |
| dfont  | <YesIcon /> | <NoIcon />  | <NoIcon />  |
| eot    | <NoIcon />  | <NoIcon />  | <YesIcon /> |
| fon    | <NoIcon />  | <NoIcon />  | <NoIcon />  |
| otf    | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| ps     | <NoIcon />  | <NoIcon />  | <NoIcon />  |
| svg    | <NoIcon />  | <NoIcon />  | <YesIcon /> |
| ttc    | <NoIcon />  | <NoIcon />  | <NoIcon />  |
| ttf    | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| woff   | <NoIcon />  | <YesIcon /> | <YesIcon /> |
| woff2  | <NoIcon />  | <YesIcon /> | <YesIcon /> |
### Platform built-in fonts
If you don't want to use a custom font by specifying a `fontFamily`, platform's default font will be used. Each platform has a set of built in fonts. On Android, the default font is Roboto. On iOS, it's SF Pro.
A platform's default font is usually easy-to-read. However, don't be surprised when the system default font is changed to use another font that is not easy to read. In this case, use your custom font so you have precise control over what the user will see.
### Handle `@expo/vector-icons` initial load
When the icons from `@expo/vector-icons` library load for the first time, they appear as invisible icons in your app. Once they load, they're cached for all the app's subsequent usage. To avoid showing invisible icons on your app's first load, preload during the initial loading screen with [`useFonts`](/versions/latest/sdk/font/#usefontsmap). For example:
```tsx app/_layout.tsx
import { useFonts } from 'expo-font';
import Ionicons from '@expo/vector-icons/Ionicons';
export default function RootLayout() {
  useFonts([require('./assets/fonts/Inter-Black.otf', Ionicons.font)]);
  return (
  )
}
```
Now, you can use any icon from the `Ionicons` library in a React component:
```tsx
<Ionicons name="checkmark-circle" size={32} color="green" />
```
### Loading a remote font directly from the web
> **warning** **If you're loading remote fonts, make sure they are being served from an origin with CORS properly configured**. If you don't do this, your remote font might not load properly on the web platform.
Loading fonts from a local asset is the safest way to load a font in your app. When including fonts as local assets, after you submit your app to the app stores, these fonts are bundled with the app download and will be available immediately. You don't have to worry about CORS or other potential issues.
However, loading a font file directly from web is done by replacing the `require('./assets/fonts/FontName.otf')` with the URL of your font as shown in the example below.
#### Using a remote font
```tsx
import { useFonts } from 'expo-font';
import { Text, View, StyleSheet } from 'react-native';
export default function App() {
  const [loaded, error] = useFonts({
    'Inter-SemiBoldItalic': 'https://rsms.me/inter/font-files/Inter-SemiBoldItalic.otf?v=3.12',
  });
  if (!loaded || !error) {
    return null;
  }
  return (
    <View style={styles.container}>
      <Text style={{ fontFamily: 'Inter-SemiBoldItalic', fontSize: 30 }}>Inter SemiBoldItalic</Text>
      <Text style={{ fontSize: 30 }}>Platform Default</Text>
    </View>
  );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
});
```


## Assets

Learn about using static assets in your project, including images, videos, sounds, database files, and fonts.

A **static asset** is a file that is bundled with your app's binary (native binary). This file type is not part of your app's JavaScript bundle which contain your app's code. Common types of static assets include images, videos, sounds, database files for SQLite, and fonts. These assets can be served locally from your project or remotely over the network.
This guide covers different ways you can load and use static assets in your project and also provides additional information on how to optimize and minify assets.
## Serve an asset locally
When an asset is stored in your project's file system, it can be embedded in your app binary at build time or loaded at runtime. You can import it like a JavaScript module using `require` or `import` statements.
For example, to render an image called **example.png** in **App.js**, you can use `require` to import the image from the project's **assets/images** directory and pass it to the `<Image>` component:
```tsx app/index.tsx
<Image source={require('./assets/images/example.png')} />
```
In the above example, the bundler reads the imported image's metadata and automatically provides the width and height. For more information, see [Static Image Resources](https://reactnative.dev/docs/images#static-image-resources).
Libraries such as `expo-image` and `expo-file-system` work similarly to the `<Image>` component with local assets.
### How are assets served locally
Locally stored assets are served over HTTP in development. They are automatically bundled into your app binary at the build time for production apps and served from disk on a device.
### Load an asset at build time with `expo-asset` config plugin
To load an asset at build time, you can use the [config plugin](/versions/latest/sdk/asset/#example-appjson-with-config-plugin) from the `expo-asset` library. This plugin will embed the asset file in your native project.
Step 1: 
Install the `expo-asset` library.
```sh
$ npx expo install expo-asset
```
Step 2: 
Add the config plugin to your project's [app config](/versions/latest/config/app/#plugins) file. The configuration must contain the path to the asset file using [`assets`](/versions/latest/sdk/asset/#configurable-properties) property which takes an array of one or more files or directories to link to the native project.
The path to each asset file must be relative to your project's root since the app config file is located in the project's root directory.
```json app.json
{
  "expo": {
    "plugins": [
      [
        "expo-asset",
        {
          "assets": ["./assets/images/example.png"]
        }
      ]
    ]
  }
}
```
Step 3: 
After embedding the asset with the config plugin, [create a new development build](/develop/development-builds/create-a-build/). Now, you can import and use the asset in your project without using a `require` or an `import` statement.
For example, the **example.png** is linked by the above config plugin. You can directly import it into your component and use its resource name as the URI. Note that when rendering assets without using `require`, you need to explicitly provide a width / height.
```tsx app/index.tsx
import { Image } from 'expo-image';
export default function HomeScreen() {
  return <Image source={{ uri: 'example' }} style={{ width: 100, height: 100 }} />;
}
```
> **info** Different file formats are supported with the `expo-asset` config plugin. For more information on these formats, see [Assets API reference](/versions/latest/sdk/asset/#configurable-properties). If you don't see a file format supported by the config plugin, you can use the [`useAssets`](#load-an-asset-at-runtime-with-useassets-hook) hook to load the asset at runtime.
### Load an asset at runtime with `useAssets` hook
The `useAssets` hook from `expo-asset` library allows loading assets asynchronously. This hook downloads and stores an asset locally and after the asset is loaded, it returns a list of that asset's instances.
Step 1: 
Install the `expo-asset` library.
```sh
$ npx expo install expo-asset
```
Step 2: 
Import the [`useAssets`](/versions/latest/sdk/asset/#useassetsmoduleids) hook from the `expo-asset` library in your screen component:
```tsx app/index.tsx
import { useAssets } from 'expo-asset';
export default function HomeScreen() {
  const [assets, error] = useAssets([
    require('path/to/example-1.jpg'),
    require('path/to/example-2.png'),
  ]);
  return assets ? <Image source={assets[0]} /> : null;
}
```
## Serve an asset remotely
When an asset is served remotely, it is not bundled into the app binary at build time. You can use the URL of the asset resource in your project if it is hosted remotely. For example, pass the URL to the `<Image>` component to render a remote image:
```jsx App.js
import { Image } from 'expo-image';
function App() {
  return (
    <Image source={{ uri: 'https://example.com/logo.png' }} style={{ width: 50, height: 50 }} />
  );
}
```
There is no guarantee about the availability of images served remotely using a web URL because an internet connection may not be available, or the asset might be removed.
Additionally, loading assets remotely also requires you to provide an asset's metadata. In the above example, since the bundler cannot retrieve the image's width and height, those values are passed explicitly to the `<Image>` component. If you don't, the image will default to 0px by 0px.
## Additional information
### Manual optimization methods
#### Images
You can compress images using the following:
- [`guetzli`](https://github.com/google/guetzli)
- [`pngcrush`](https://pmt.sourceforge.io/pngcrush/)
- [`optipng`](http://optipng.sourceforge.net/)
Some image optimizers are lossless. They re-encode your image to be smaller without any change or loss in the pixels displayed. When you need each pixel to be untouched from the original image, a lossless optimizer and a lossless image format like PNG are a good choice.
Other image optimizers are lossy. The optimized image differs from the original image. Often, lossy optimizers are more efficient because they discard visual information that reduces file size while making the image look nearly identical to humans. Tools like `imagemagick` can use comparison algorithms like [SSIM](https://en.wikipedia.org/wiki/Structural_similarity) to show how similar two images look. It's quite common for an optimized image that is over 95% similar to the original image to be far less than 95% of the original file size.
#### Other assets
For assets like GIFs or videos, or non-code and non-image assets, it's up to you to optimize and minify those assets.
> **Note**: GIFs are a very inefficient format. Modern video codecs can produce significantly smaller file sizes with better quality.
### Fonts
See [Add a custom font](/develop/user-interface/fonts/#add-a-custom-font) for more information on how to add a custom font to your app.


## Color themes

Learn how to support light and dark modes in your app.

It's common for apps to support light and dark color schemes. Here is an example of how supporting both modes looks in an Expo project:
## Configuration
> **info** For Android and iOS projects, additional configuration is required to support switching between light and dark mode. For web, no additional configuration is required.
To configure supported appearance styles, you can use the [`userInterfaceStyle`](/versions/latest/config/app/#userinterfacestyle) property in your project's [app config](/versions/latest/config/app). By default, this property is set to `automatic` when you create a new project with the [default template](/get-started/create-a-project/).
Here is an example configuration:
```json app.json
{
  "expo": {
    "userInterfaceStyle": "automatic"
  }
}
```
You can also configure `userInterfaceStyle` property for a specific platforms by setting either [`android.userInterfaceStyle`](/versions/latest/config/app/#userinterfacestyle-2) or [`ios.userInterfaceStyle`](/versions/latest/config/app/#userinterfacestyle-1) to the preferred value.
> **info** The app will default to the `light` style if this property is absent.
When you are creating a development build, you have to install [`expo-system-ui`](/versions/latest/sdk/system-ui/#installation) to support the appearance styles for Android. Otherwise, the `userInterfaceStyle` property is ignored.
```sh
$ npx expo install expo-system-ui
```
If the project is misconfigured and doesn't have `expo-system-ui` installed, the following warning will be shown in the terminal:
```sh
Â» android: userInterfaceStyle: Install expo-system-ui in your project to enable this feature.
```
You can also use the following command to check if the project is misconfigured:
```sh
$ npx expo config --type introspect
```
Note: Using bare React Native app?
---
#### Android
Ensure that the `uiMode` flag is present on your `MainActivity` (and any other activities where this behavior is desired) in **AndroidManifest.xml**:
```xml
<activity android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode">
```
Implement the `onConfigurationChanged` method in **MainActivity.java**:
```java
import android.content.Intent;
import android.content.res.Configuration;
public class MainActivity extends ReactActivity {
  @Override
  public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    Intent intent = new Intent("onConfigurationChanged");
    intent.putExtra("newConfig", newConfig);
    sendBroadcast(intent);
  }
}
```
#### iOS
You can configure supported styles with the [`UIUserInterfaceStyle`](https://developer.apple.com/documentation/bundleresources/information_property_list/uiuserinterfacestyle) key in your app **Info.plist**. Use `Automatic` to support both light and dark modes.
---
### Supported appearance styles
The `userInterfaceStyle` property supports the following values:
- `automatic`: Follow system appearance settings and notify about any change the user makes.
- `light`: Restrict the app to support light theme only.
- `dark`: Restrict the app to support dark theme only.
## Detect the color scheme
To detect the color scheme in your project, use `Appearance` or `useColorScheme` from `react-native`:
```tsx app/index.tsx
import { Appearance, useColorScheme } from 'react-native';
```
Then, you can use `useColorScheme()` hook as shown below:
```tsx app/index.tsx
function MyComponent() {
  let colorScheme = useColorScheme();
  if (colorScheme === 'dark') {
    // render some dark thing
  } else {
    // render some light thing
  }
}
```
In some cases, you will find it helpful to get the current color scheme imperatively with [`Appearance.getColorScheme()` or listen to changes with `Appearance.addChangeListener()`](https://reactnative.dev/docs/appearance).
## Additional information
### Minimal example
#### useColorScheme example
```tsx
import { StatusBar } from 'expo-status-bar';
export default function App() {
  const colorScheme = useColorScheme();
  const themeTextStyle = colorScheme === 'light' ? styles.lightThemeText : styles.darkThemeText;
  const themeContainerStyle =
    colorScheme === 'light' ? styles.lightContainer : styles.darkContainer;
  return (
    <View style={[styles.container, themeContainerStyle]}>
      <Text style={[styles.text, themeTextStyle]}>Color scheme: {colorScheme}</Text>
      <StatusBar />
    </View>
  );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  text: {
    fontSize: 20,
  },
  lightContainer: {
    backgroundColor: '#d0d0c0',
  },
  darkContainer: {
    backgroundColor: '#242c40',
  },
  lightThemeText: {
    color: '#242c40',
  },
  darkThemeText: {
    color: '#d0d0c0',
  },
});
```
### Tips
While you are developing your project, you can change your simulator's or device's appearance by using the following shortcuts:
- If using an Android Emulator, you can run `adb shell "cmd uimode night yes"` to enable dark mode, and `adb shell "cmd uimode night no"` to disable dark mode.
- If using a physical Android device or an Android Emulator, you can toggle the system dark mode setting in the device's settings.
- If working with an iOS emulator locally, you can use the <kbd>Cmd â</kbd> + <kbd>Shift</kbd> + <kbd>a</kbd> shortcut to toggle between light and dark modes.


## Animation

Learn how to integrate React Native animations and use it in your Expo project.

Animations are a great way to enhance and provide a better user experience. In your Expo projects, you can use the [Animated API](https://reactnative.dev/docs/next/animations) from React Native. However, if you want to use more advanced animations with better performance, you can use the [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/) library. It provides an API that simplifies the process of creating smooth, powerful, and maintainable animations.
## Installation
You can skip installing `react-native-reanimated` if you have created a project using [the default template](/get-started/create-a-project/). This library is already installed. Otherwise, install it by running the following command:
```sh
$ npx expo install react-native-reanimated
```
## Usage
### Minimal example
The following example shows how to use the `react-native-reanimated` library to create a simple animation. For more information on the API and advanced usage, see [`react-native-reanimated` documentation](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/your-first-animation).
#### Using react-native-reanimated
```tsx
import Animated, {
  useSharedValue,
  withTiming,
  useAnimatedStyle,
  Easing,
} from 'react-native-reanimated';
import { View, Button, StyleSheet } from 'react-native';
export default function AnimatedStyleUpdateExample() {
  const randomWidth = useSharedValue(10);
  const config = {
    duration: 500,
    easing: Easing.bezier(0.5, 0.01, 0, 1),
  };
  const style = useAnimatedStyle(() => {
    return {
      width: withTiming(randomWidth.value, config),
    };
  });
  return (
    <View style={styles.container}>
      <Animated.View style={[styles.box, style]} />
      <Button
        title="toggle"
        onPress={() => {
          randomWidth.value = Math.random() * 350;
        }}
      />
    </View>
  );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  box: {
    width: 100,
    height: 80,
    backgroundColor: 'black',
    margin: 30,
  },
});
```
## Other animation libraries
You can use other animation packages such as [Moti](https://moti.fyi/) in your Expo project. It works on Android, iOS, and web.


## Store data

Learn about different libraries available to store data in your Expo project.

Storing data can be essential to the features implemented in your mobile app. There are different ways to save data in your Expo project depending on the type of data you want to store and the security requirements of your app. This page lists a variety of libraries to help you decide which solution is best for your project.
## Expo SecureStore
`expo-secure-store` provides a way to encrypt and securely store key-value pairs locally on the device.
## Expo FileSystem
`expo-file-system` provides access to a file system stored locally on the device. Within Expo Go, each project has a separate file system and no access to other Expo projects' files. However, it can save content shared by other projects to the local filesystem and share local files with other projects. It is also capable of uploading and downloading files from network URLs.
## Expo SQLite
`expo-sqlite` package gives your app access to a database that can be queried through a WebSQL-like API. The database is persisted across restarts of your app. You can use it for importing an existing database, opening databases, creating tables, inserting items, querying and displaying results, and using prepared statements.
## Async Storage
[Async Storage](https://react-native-async-storage.github.io/async-storage/) is an asynchronous, unencrypted, persistent key-value storage for React Native apps. It has a simple API and is a good choice for storing small amounts of data. It is also a good choice for storing data that does not need encryption, such as user preferences or app state.
## Other libraries
There are other libraries available for storing data for different purposes. For example, you might not need encryption in your project or are looking for a faster solution similar to Async Storage.
We recommend checking out [React Native for a list of libraries](https://reactnative.directory/?search=storage) to help you store your project's data.


## Next steps

A list of useful resources to learn more about implementing navigation and UI in your app.