# Custom icon fonts with React Native

When working with icons in [React Native](https://facebook.github.io/react-native/) apps we're spoilt for choice with a wide range of free and open-source icon sets such as [FontAwesome](https://fontawesome.com/), [Material](https://www.google.com/design/icons/) and [Ionicons](https://ionicons.com/). To make things even easier, the wonderful [react-native-vector-icons](https://github.com/oblador/react-native-vector-icons) 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](https://expo.io/) 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](https://docs.expo.io/versions/latest/guides/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](https://yarnpkg.com/en/) 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](https://icomoon.io/) 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:

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

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