Using Expo Custom Fonts with react-navigation

I’m using CRNA together with react-navigation. My App has a bottom TabNavigator with 5 Tabs, each of which have a Font Icon loaded from a icomoon.ttf file using the steps from the Expo Custom Fonts tutorial. However, I’m getting a warning in the Terminal about Expo.Font not being available. I think the reason is that App.js imports the Routes component, which imports the Screen components, which imports the FontIcon component. The FontIcon import triggers the call to createIconSetFromIcomoon() call, which fires before the fonts can be loaded. Is there any way to avoid this?:

App.js

...
import Routes from './routes'
...

class App extends Component {
  state = { fontLoaded: false };

  async componentWillMount() {
    try {
      await FontLoader.load();
      this.setState({ fontLoaded: true });
    } catch (err) {
      // console.log(err);
    }
  }

  render() {
    return (
      this.state.fontLoaded
        ? <Routes />
        : <Text>Loading Fonts...</Text>
    );
  }
}

Routes.js

...
const MainScreenNavigator = TabNavigator({
  MainScreen: { screen: MainScreen },
},
...

MainScreen.js

const MainScreen = () => {
  static navigationOptions = {
    title: 'Home',
    tabBarIcon: ({ tintColor }) => (
      <FontIcon
        name="icon_dashboard"
        size={24}
        color={tintColor}
      />
    ),
  }
}

FontIcon.js

import React from 'react';
import PropTypes from 'prop-types';
import { createIconSetFromIcoMoon } from '@expo/vector-icons';

import icoMoonConfig from '../../../../assets/fonts/icomoon-config.json';

const Icon = createIconSetFromIcoMoon(icoMoonConfig, 'icons');

const FontIcon = (props) => {
  const { name, size, color } = props;

  return (
    <Icon
      name={name}
      size={size}
      color={color}
    />
  );
};

For anyone still looking for this solution, I was able to do this by rewriting the FontIcon component like this.

import React from 'react';
import { Font } from 'expo';
import { View } from 'react-native';

import { createIconSetFromIcoMoon } from '@expo/vector-icons';
import icoMoonConfig from '../assets/fonts/icomoon-config.json';
const Icon = createIconSetFromIcoMoon(icoMoonConfig, 'Icomoon');

export default class FontIcon extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      fontLoaded: false
    }
  }

  async componentDidMount() {
    await Font.loadAsync({
      'Icomoon': require('../assets/fonts/icomoon.ttf')
    });

    this.setState({fontLoaded: true});


  }

  render() {
    return (
      <View>
      {
        this.state.fontLoaded && (
          <Icon
            name={this.props.name}
            size={this.props.size}
            color={this.props.color}
          />
        )
      }
      </View>
    );
  }
}
1 Like
import { Asset } from 'expo-asset';
import * as Font from 'expo-font';
import React, { useState } from 'react';
import { Platform, StatusBar, StyleSheet, View } from 'react-native';
import { Ionicons, createIconSetFromIcoMoon } from '@expo/vector-icons';
import icoMoonConfig from './assets/fonts/selection.json';
// import { createIconSetFromIcoMoon } from 'react-native-vector-icons'; // You can also used from this lib.
const Icon = createIconSetFromIcoMoon(
    icoMoonConfig,
    'icomoon',
    'icomoon.ttf'
);
import AppNavigator from './navigation/AppNavigator';

export default function App(props) {
  const [isLoadingComplete, setLoadingComplete] = useState(false);

  if (!isLoadingComplete && !props.skipLoadingScreen) {
    return (
      <AppLoading
        startAsync={loadResourcesAsync}
        onError={handleLoadingError}
        onFinish={() => handleFinishLoading(setLoadingComplete)}
      />
    );
  } else {
    return (
      <View style={styles.container}>
        {Platform.OS === 'ios' && <StatusBar barStyle="default" />}
          <Icon name="maps" size={32} color="red" />
          <AppNavigator />
      </View>
    );
  }
}

async function loadResourcesAsync() {
  await Promise.all([
    Asset.loadAsync([
      require('./assets/images/robot-dev.png'),
      require('./assets/images/robot-prod.png'),
    ]),
    Font.loadAsync({

      // This is the font that we are using for our tab bar
      ...Ionicons.font,
      // We include SpaceMono because we use it in HomeScreen.js. Feel free to
      // remove this if you are not using it in your app
      'space-mono': require('./assets/fonts/SpaceMono-Regular.ttf'),
      'icomoon': require('./assets/fonts/icomoon.ttf'),
    }),
  ]);
}

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

function handleFinishLoading(setLoadingComplete) {
  setLoadingComplete(true);
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
});
1 Like