Custom icon fonts with React Native
Tags: reactnative, javascript, expo, react
Published: 2019-07-17 09:26:08

---
title: Custom icon fonts with React Native
published: true
description: 
tags: reactnative, javascript, expo, react
canonical_url: https://www.orangejellyfish.com/blog/custom-icon-fonts-with-react-native/
---

When working with icons in [React Native][rn] apps we're spoilt for choice with
a wide range of free and open-source icon sets such as [FontAwesome][fa],
[Material][material] and [Ionicons][ion]. To make things even easier, the
wonderful [react-native-vector-icons][rnvi] project bundles all of those icon
sets plus more into one single package. But sometimes free and open-source icon
sets just don't cut it and you're left wondering how to achieve something that
has the same developer experience for a custom icon set. Fortunately,
react-native-vector-icons and a bunch of other projects have us covered here
too.

## Setting up react-native-vector-icons

If you're using [Expo][expo] and have not ejected to ExpoKit then there's
nothing to do here. Expo bundles a wrapper around react-native-vector-icons in
the [`@expo/icons` package][expo-icons].

Otherwise, the installation of the react-native-vector-icons package is as you
would expect for a React Native app. It's published to npm so you can add it to
your project with the CLI or equivalent (we tend to use [Yarn][yarn] when
working with React Native because it plays better with Expo):

```sh
$ yarn add react-native-vector-icons
$ react-native link react-native-vector-icons
```

## Generating an icon font

With react-native-vector-icons set up in your project you are ready to work on
the icons themselves. In our experience [IcoMoon][icomoon] is the most effective
tool here. IcoMoon is a web application that allows you to import SVG files and
produce font files in various formats from collections of those SVGs, as shown
in the following screenshot:

![Creating an icon set in IcoMoon](https://thepracticaldev.s3.amazonaws.com/i/cyx35jc9fzeg6sf60yxu.png)
*An example of creating an icon set in IcoMoon*

Once all of your icons are imported to the IcoMoon app you can select them and
"Generate" the font file (note that in the screenshot below it shows the number
of selected icons to the left of the highlighted "Generate Font" button):

![Generating an icon font in IcoMoon](https://thepracticaldev.s3.amazonaws.com/i/70dyk98eo23hdpqssebw.png)
*An example of generating an icon font from an icon set in IcoMoon*

There are a few options to configure the resulting font but most of the time the
defaults will suffice. When you're happy download the bundle and unzip it to
find a selection of font files, some examples and a `selection.json` file. It's
that file plus the `*.ttf` font file that we need. Copy those files to a
sensible directory within your React Native codebase. We usually go for a top-
level `assets` directory which contains all of the static assets used by the app
including fonts and images.

## Using the custom icon font

It's recommended that you pre-load any fonts that your app is going to use and
your new custom icon font is no exception. In your main app entry point you can
use the `Font.loadAsync` method. If you have used the Expo CLI to initialise
your project then you probably have something that looks like this already:

```js
import React from 'react';
import { registerRootComponent, AppLoading } from 'expo';
import * as Font from 'expo-font';

class App extends React.Component {
  state = {
    isLoadingComplete: false,
  };

  loadResourcesAsync = async () => Promise.all([
    Font.loadAsync({
      'custom-icons': require('../assets/fonts/custom-icons.ttf'),
    }),
  ]);

  handleLoadingError = (error) => {
    // In this case, you might want to report the error to your error
    // reporting service, for example Sentry
    console.warn(error);
  };

  handleFinishLoading = () => {
    this.setState({ isLoadingComplete: true });
  };

  render() {
    const { isLoadingComplete } = this.state;

    if (!isLoadingComplete) {
      return (
        <AppLoading
          startAsync={this.loadResourcesAsync}
          onError={this.handleLoadingError}
          onFinish={this.handleFinishLoading}
        />
      );
    }

    return (
      <App />
    );
  }
}

registerRootComponent(App);

// Export the App component for unit testing purposes. Expo handles rendering
// via the "registerRootComponent" call above and does not require an export.
export default App;
```

With this configuration your custom icon font file will be loaded at app start-
up rather than at first usage which would otherwise result in flashes of
unstyled (or missing) content.

Next up you need a normal React component to render icons from your new font.
The react-native-vector-icons package provides some utility methods to make this
process simpler. The following few lines are all that are needed. We usually
place this in a `src/components/icon/index.js` file:

```js
import { createIconSetFromIcoMoon } from 'react-native-vector-icons';
import icoMoonConfig from '../../../assets/fonts/selection.json';

// We use the IcoMoon app (https://icomoon.io) to generate a custom font made up
// of SVG icons. The actual font file is loaded up-front in src/index.js.
export default createIconSetFromIcoMoon(icoMoonConfig, 'custom-icons');
```

The key points to note here are the import of the `selection.json` file from the
bundle downloaded from IcoMoon and the name of the font, `custom-icons`, as
defined in the `Font.loadAsync` call in the main app entry point.

The `createIconSetFromIcoMoon` function could be thought of as a factory that
returns a React component. You can now import that component from your other
components to render icons. The following example imagines a simple "button"
component in `src/components/button/index.js`:

```js
import React from 'react';
import { TouchableOpacity, Text } from 'react-native';
import Icon from '../icons';

const Button = () => (
  <TouchableOpacity>
    <Icon name="settings" />
    <Text>Settings</Text>
  </TouchableOpacity>
);

export default Button;
```

The new `Icon` component supports all of the props that the open-source icon
sets bundled with react-native-vector-icons support. This means you can apply
custom styles, such as sizes and colours, from React Native stylesheets.

[rn]: https://facebook.github.io/react-native/
[fa]: https://fontawesome.com/
[material]: https://www.google.com/design/icons/
[ion]: https://ionicons.com/
[rnvi]: https://github.com/oblador/react-native-vector-icons
[expo]: https://expo.io/
[yarn]: https://yarnpkg.com/en/
[expo-icons]: https://docs.expo.io/versions/latest/guides/icons/
[icomoon]: https://icomoon.io/