DEV Community

Cover image for How to add custom fonts to a React Native project with Expo and React Navigation!
Imad
Imad

Posted on

How to add custom fonts to a React Native project with Expo and React Navigation!

To achieve our goal, we will take the following steps :

  • Generate a new test project with Expo-CLI.
  • Install and import react-navigation, react-navigation-stack modules.
  • Create 2 screens and display some dummy text.
  • Download a Font and add it to the project.
  • Import and use loadAsync helper from Expo
  • Wire up the newly added font and use it in the project.

1- Generate a new Expo project

Head over to a directory of your choice and run :

Using npx: npx expo-cli init test-custom-font
OR
Using expo-cli: expo init test-custom-font

2- Install the dependencies

run the following to install react-navigation dependencies:

npm i react-navigation react-navigation-stack react-navigation-gesture-handler

While the installation is running, let’s open the project and add some boilerplate.

3- Create the screens and display some text

To keep this article short, i will skip the how-to-create-and-import-export-your-components section, and head over to the adding the Font.

At this point, your files should look like this :

App.js


import { createAppContainer } from "react-navigation";
import { createStackNavigator } from "react-navigation-stack";
import HomeScreen from "./src/screens/HomeScreen";
import DetailScreen from "./src/screens/DetailScreen";
const AppNavigation  = createStackNavigator(
  {
    Home: HomeScreen,
    Details: DetailScreen
  }
);
export default createAppContainer(AppNavigation);

Enter fullscreen mode Exit fullscreen mode

HomeScreen.js

import React from "react";
import { View, Text, StyleSheet, Button } from "react-native";

const HomeScreen = ({ navigation }) => {
  return (
    <View style={styles.container}>
      <Text style={styles.textStyle}> Welcome to the Home Screen </Text>
      <Button
        title="See Details"
        onPress={() => navigation.navigate("Details")}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center"
  }
});

export default HomeScreen;

Enter fullscreen mode Exit fullscreen mode

DetailScreen.js

import React from "react";
import { View, Text, StyleSheet } from "react-native";

const DetailScreen = () => {
  return (
    <View style={styles.container}>
      <Text style={styles.textStyle}>
        Lorem Ipsum is simply dummy text of the printing and typesetting
        industry. Lorem Ipsum has been the industry's standard dummy text ever
        since the 1500s, when an unknown printer took a galley of type and
        scrambled it to make a type specimen book. It has survived not only five
        centuries, but also the leap into electronic typesetting, remaining
        essentially unchanged. It was popularised in the 1960s with the release
        of Letraset sheets containing Lorem Ipsum passages, and more recently
        with desktop publishing software like Aldus PageMaker including versions
        of Lorem Ipsum.
      </Text>
    </View>
  );
};
const styles = StyleSheet.create({
  container: {
    padding: 12,
    flex: 1,
    justifyContent: "center",
    alignItems: "center"
  }
});
export default DetailScreen;
Enter fullscreen mode Exit fullscreen mode

run expo start the result should look like this :

Alt Text

3- Download a Font and add it to the project.

  • Inside the assets folder, create a fonts folder.
  • Head over to google fonts.
  • Download and unzip a font of your choice in any location on your machine.
  • Copy/paste the .ttf file inside the font folder in the project.
  • In this demo we will use "montserrat"

By now, the project structure should look like this :

Alt Text

3- Import Expo Font module and wire up the custom font.

Depending on whether you are using classes or functional components, loading the font is slightly different, let's have a look at both :

According to Expo documentation, loading a custom font should be done using the built-in Font.loadAsync helper method, and since "as it's name suggests" its an async function, we should invoke it inside a life cycle method.


Class based approach

The current implementation of our App.js does not support a life cycle method, as the root component (App.js line 11)is created and exported immediately.

Likely for us, the only thing Expo expects from our App.js is a valid React component.
So let’s build and export a custom App component with our loaded font.

Your App.js should look like this now,

// import React 
import React, { Component } from "react";
// import Expo Font module 
import * as Font from "expo-font";
import { createAppContainer } from "react-navigation";
import { createStackNavigator } from "react-navigation-stack";
import HomeScreen from "./src/screens/HomeScreen";
import DetailScreen from "./src/screens/DetailScreen";
// import AppLoading helper 
//https://docs.expo.io/versions/latest/sdk/app-loading/
import { AppLoading } from "expo";

const appNavigator = createStackNavigator(
  {
    Home: HomeScreen,
    Details: DetailScreen
  },
  {
    initialRouteName: "Home"
  }
);

// instead of immediately exporting the AppNavigator component we assign in to a constant. 
const RootApp = createAppContainer(appNavigator);


// we create and export our own custom App component 
export default class App extends Component {

  state = {
    loaded: false
  };
// create a helper function to load the font 
  _loadFontsAsync = async () => {
// loadAsync returns true | error
    let isLoaded = await Font.loadAsync({
      // add as many fonts as you want here .... 
      Montserrat: require("./assets/fonts/montserrat.ttf")
    });
    this.setState({ loaded: isLoaded });
  };

// call _loadFontsAsync 
  componentDidMount() {
    this._loadFontsAsync();
  }

  render() {
    if (!this.state.loaded) {
      return <AppLoading />;
    }
    // from the custom App we return the component we assigned to RootApp.
    return <RootApp />;
  }
}
Enter fullscreen mode Exit fullscreen mode

Functional approach

In functional components, we can make use of React hooks to solve this problem, likely for us, a font loading hook already exist and we do not have to build our own.

We will make use of @use-expo/font from Expo to load our Font.

lets install the package first, run npm i @use-expo/font

Next, let's implement it :

// import React 
import React from "react";
// import Expo Font module 
import * as Font from "expo-font";
// import useFonts hook  
import { useFonts } from "@use-expo/font";
import { createAppContainer } from "react-navigation";
import { createStackNavigator } from "react-navigation-stack";
import HomeScreen from "./src/screens/HomeScreen";
import DetailScreen from "./src/screens/DetailScreen";
// import AppLoading helper 
//https://docs.expo.io/versions/latest/sdk/app-loading/
import { AppLoading } from "expo";

const appNavigator = createStackNavigator(
  {
    Home: HomeScreen,
    Details: DetailScreen
  },
  {
    initialRouteName: "Home"
  }
);

// instead of immediately exporting the AppNavigator component we assign in to a constant. 
const RootApp = createAppContainer(appNavigator);
// require in the font 
const customFonts = {
  Montserrat: require("./assets/fonts/montserrat.ttf"),
};

const App = () => {
    // the same as Font.loadAsync , the hook returns  true | error 
    const [isLoaded] = useFonts(customFonts);


    if (!isLoaded) {
        return <AppLoading />;
    }
    // from the custom App we return the component we assigned to RootApp.
    return <RootApp />;

}

export default App
Enter fullscreen mode Exit fullscreen mode

As you can see, the functional approach is way cleaner and more readable.


5- Use the newly added font:

Now, all we have to do is add the font family to our style object, in both HomeScreen.js and DetailScreen.js :

textStyle:{ fontFamily:'Montserrat'}

Result:

Alt Text


Like this post ? let me know, i will be posting about advanced topics on React, React Native or Node.js.

You can find me on twitter too ! :)

Top comments (7)

Collapse
 
caspergeek profile image
Eranda K. • Edited

guess import { AppLoading } from "expo"; is no longer supported. You may have to use expo install expo-app-loading

Collapse
 
2imad profile image
Imad

Hello Eranda,
Thank you for the heads up!
The article is getting a bit old, and a lot has changed since then.
I will give it an update when I have some time

Cheers

Imad

Collapse
 
coderdev27 profile image
coderdev27

Thanks bro!

Collapse
 
mahdimohammadii profile image
Mahdi

How can I add fonts other than google fonts. when I try to add fonts from my language my project breaks! any suggestion?!

Collapse
 
2imad profile image
Imad

Hi Mahdi,
You can add any font as long as you have a ".ttf" file to add to your project.
It does not have to be a google font.
If you need help, DM me on twitter and share with me your project repo, and I will help you!
Cheers,

IYO

Collapse
 
mahdimohammadii profile image
Mahdi

Hi IYO.
wanted to text you but I thought that maybe I should try one more time. Fortunately it worked! the problem was that the font's name had " " instead of "-".
like: "b titr Bold" but I renamed it to "titr" and it worked! (I guess if I used "b-titr-bold" it would work too!)

Thanks!

Mahdi

Collapse
 
coderdev27 profile image
coderdev27

Thanks a ton!