This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.

Fonts

Edit page

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 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

A variable font holds many faces in one file. Instead of shipping one file per weight, you ship one file and select a face with the fontWeight and fontStyle style props. Both the config plugin and the useFonts hook accept variable fonts.

Each platform decides which faces a file offers in a different way, so check the file before you rely on it:

  • Android reads the wght axis and draws each weight fontWeight can name from it, limited to the range the axis declares. This needs Android 10 or later. On earlier versions the font renders at its default weight, and Android creates the bold by thickening the letters.
  • iOS reads the named instances the file declares. A named instance is a face the font designer gave a name and a fixed position on each axis, such as "Bold" or "Condensed Light". React Native selects the named instance whose weight is closest to the one you request. A weight that no named instance covers is out of reach. If the file declares no named instances, only its default face renders.

For fontStyle: 'italic', Android applies the font's ital axis on Android 15 and later. On earlier versions, Android creates the italic by slanting the upright letters. iOS uses the italic named instances in the file.

On React Native 0.88 (Expo SDK 58) and later, you can also set any axis directly on a component with the fontVariationSettings style prop, such as fontVariationSettings: "'wght' 650, 'wdth' 90". It works on <Text> and <TextInput> on both platforms (Android 8 and newer), and the axes you set there take precedence over fontWeight and fontStyle.

Inspect a file's axes and named instances with a utility such as fontTools. If a face you need is out of reach on a platform, use fontTools to extract that axis configuration and save it as a separate static 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.

Two ways to use the local font file in your project:

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.

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:

To embed a font in a project, follow the steps below:

1

After adding a custom font file in your project, install the expo-font library.

Terminal
- npx expo install expo-font
- yarn expo install expo-font
- pnpm expo install expo-font
- bun expo install expo-font

2

Add the config plugin to your app config file. The configuration must contain the path to the font file using fonts, android or ios 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. 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.

If you want to refer to fonts using a combination of fontFamily, weight, and style, provide an array of objects (see Inter below).

For a variable font on Android, declare the file path in each fontDefinitions entry, or once next to fontFamily to avoid repeating it. Each definition needs a weight and style pair that no other definition in the same family uses, because Android resolves a family by that pair.

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 } ] }, { "fontFamily": "Roboto Flex", "path": "./assets/fonts/RobotoFlex.ttf", "fontDefinitions": [ { "weight": 400 }, { "weight": 700 }, { "weight": 400, "style": "italic", "axes": { "slnt": -10 } } ] } ] }, "ios": { "fonts": [ "./assets/fonts/Inter-Bold.ttf", "./assets/fonts/Inter-BoldItalic.ttf", "./assets/fonts/RobotoFlex.ttf" ] } } ] ] } }

style selects the face that fontStyle matches, but it does not slant the glyphs. To get an italic out of an upright file, set a slant axis in axes, such as slnt or ital.

axes accepts any variation axis the font declares, such as wdth for a condensed face. Tags are four characters long and case sensitive. The registered axes (ital, opsz, slnt, wdth, wght) are lowercase, and a font's own axes are uppercase, such as GRAD. weight sets wght unless you override it in axes.

On iOS, path and axes do not apply. List the variable font file in ios.fonts as you would a static font. The faces you can select are the named instances the file declares. To reach other positions on an axis, use the fontVariationSettings style prop on React Native 0.88 and later.

3

After embedding the font with the config plugin, create a new development 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.

<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> <Text style={{ fontFamily: 'Roboto Flex', fontWeight: '400', fontStyle: 'italic' }}>Roboto Flex Italic</Text>
Using this method in an existing React Native project?

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 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 or other programs.

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:

1

After adding a custom font file in your project, install the expo-font and expo-splash-screen libraries.

Terminal
- npx expo install expo-font expo-splash-screen
- yarn expo install expo-font expo-splash-screen
- pnpm expo install expo-font expo-splash-screen
- bun expo install expo-font expo-splash-screen

The expo-splash-screen library provides SplashScreen component that you can use to prevent rendering the app until the font is loaded and ready.

2

Map the font file using the useFonts hook in a top-level component such as the root layout (src/app/_layout.tsx) file in your project:

src/app/_layout.tsx
import { useFonts } from 'expo-font'; import * as SplashScreen from 'expo-splash-screen'; import {useEffect} from 'react'; SplashScreen.preventAutoHideAsync(); export default function RootLayout() { const [loaded, error] = useFonts({ 'Inter-Black': require('./assets/fonts/Inter-Black.otf'), 'Roboto Flex': require('./assets/fonts/RobotoFlex.ttf'), }); useEffect(() => { if (loaded || error) { SplashScreen.hideAsync(); } }, [loaded, error]); if (!loaded && !error) { return null; } return ( %%placeholder-start%%... %%placeholder-end%% ) }

3

Use the font on the <Text> by using fontFamily style prop in a React component:

<Text style={{ fontFamily: 'Inter-Black' }}>Inter Black</Text>

If the file you mapped is a variable font, select a face with the fontWeight and fontStyle style props:

<Text style={{ fontFamily: 'Roboto Flex', fontWeight: '300' }}>Roboto Flex Light</Text> <Text style={{ fontFamily: 'Roboto Flex', fontWeight: '700' }}>Roboto Flex Bold</Text>

useFonts reads the weight axis only. To set another axis, such as wdth or slnt, embed the font with the config plugin on Android, or set the fontVariationSettings style prop on React Native 0.88 and later.

Loading multiple weights and styles for one font family

The map syntax above treats each key as a separate font family. To load several font files (for example regular, bold, and italic cuts) under one fontFamily name, pass useFonts an array of FontFamilyDefinitions instead of a map:

src/app/_layout.tsx
const [loaded, error] = useFonts([ { fontFamily: 'Inter', fontDefinitions: [ { path: require('./assets/fonts/Inter-Regular.otf'), weight: 400 }, { path: require('./assets/fonts/Inter-Italic.otf'), weight: 400, style: 'italic' }, { path: require('./assets/fonts/Inter-Bold.otf'), weight: 700 }, ], }, ]);

Then select a face with the fontWeight and fontStyle style props:

<Text style={{ fontFamily: 'Inter', fontWeight: '700' }}>Inter Bold</Text> <Text style={{ fontFamily: 'Inter', fontWeight: '400', fontStyle: 'italic' }}>Inter Italic</Text>

Declare every face of a family in one call. A fontFamily loads once, so a later loadAsync or useFonts call for a name that is already loaded adds no faces to it. This also applies across the two call styles: after useFonts({ Inter: require('./assets/fonts/Inter-Regular.otf') }), passing an array for Inter loads nothing more.

Declare weight and style on every face of a multi-face family. Web needs both values, and explicit values keep the three platforms in agreement. A single variable font file is the exception: leave its style unset, and leave its weight unset or declare a weight range such as '100 900'. Android then instances the font's wght axis for every fontWeight instead of pinning it to one weight.

Platform notes:

  • Android uses the declared weight and style values, and falls back to the values in the font file when not declared. A declared weight pins a variable font face to that one weight, and Android synthesizes the others. Below API 29 (Android 10), only the default face loads, chosen by the declared values (closest to weight 400, upright). Fonts embedded with the config plugin use Android's XML font resources and do not have this limit.
  • iOS matches faces by the weight and style metadata embedded in the font files. It uses the declared weight and style values only to pick the family's default face when a file carries no readable metadata.
  • Web generates one CSS @font-face rule per face, with the declared weight and style values. CSS cannot read the metadata inside a font file, so a face that declares no style renders as upright. Two faces of one family that leave the same values undeclared produce the same CSS rule, and the browser renders the last one.

Use Google Fonts

Expo has first-class support for all fonts listed in Google Fonts. They are available using @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:

With expo-font config plugin

1

Install the font package. For example, to use Inter Black font, install the @expo-google-fonts/inter package with the command below.

Terminal
- npx expo install expo-font @expo-google-fonts/inter
- yarn expo install expo-font @expo-google-fonts/inter
- pnpm expo install expo-font @expo-google-fonts/inter
- bun expo install expo-font @expo-google-fonts/inter

2

Add the config plugin to your app config file. The configuration must contain the path to the font file using fonts 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.

app.json
{ "plugins": [ [ "expo-font", { "fonts": ["node_modules/@expo-google-fonts/inter/900Black/Inter_900Black.ttf"] } ] ] }

3

After embedding the font with the config plugin, create a new development 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). The example below demonstrates how to use Platform to select the correct font family name for each platform:

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

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.

1

Install the Google Fonts package, expo-font and expo-splash-screen libraries.

Terminal
- npx expo install @expo-google-fonts/inter expo-font expo-splash-screen
- yarn expo install @expo-google-fonts/inter expo-font expo-splash-screen
- pnpm expo install @expo-google-fonts/inter expo-font expo-splash-screen
- bun expo install @expo-google-fonts/inter expo-font expo-splash-screen

The expo-splash-screen library provides SplashScreen component that you can use to prevent rendering the app until the font is loaded and ready.

2

After installing the font package, map the font using the useFonts hook in a top-level component such as the root layout (src/app/_layout.tsx) file in your project:

src/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 ( %%placeholder-start%%... %%placeholder-end%% ) }

3

Use the font on the <Text> by using fontFamily style prop in a React component:

<Text style={{ fontFamily: 'Inter_900Black' }}>Inter Black</Text>

Additional information

Minimal example

expo-font usage

See usage section in Expo Fonts API reference for a minimal example of using a custom font.

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 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:

FormatAndroidiOSWeb
bdf
dfont
eot
fon
otf
ps
svg
ttc
ttf
woff
woff2

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. For example:

src/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 ( %%placeholder-start%%... %%placeholder-end%% ) }

Now, you can use any icon from the Ionicons library in a React component:

<Ionicons name="checkmark-circle" size={32} color="green" />
Icons

Learn how to use various types of icons in your Expo app, including vector icons, custom icon fonts, icon images, and icon buttons.

Loading a remote font directly from the web

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
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', }, });