Skip to main content

expo-font

Version: 55.0.4 Load custom fonts at runtime and use them in React Native components. Supports TTF, OTF, and web fonts with automatic loading and caching.

Installation

npx expo install expo-font

Usage

import { useFonts } from 'expo-font';
import { Text } from 'react-native';

function App() {
  const [loaded, error] = useFonts({
    'Inter-Black': require('./assets/fonts/Inter-Black.otf'),
    'Inter-Regular': require('./assets/fonts/Inter-Regular.otf'),
  });

  if (!loaded && !error) {
    return null;
  }

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

API Reference

useFonts Hook

useFonts(fontMap)
(fontMap: FontSource) => [boolean, Error | null]
Loads fonts and returns loading stateReturns [loaded, error] tuple:
  • loaded: true when fonts are loaded
  • error: Error object if loading failed
const [loaded, error] = useFonts({
  'CustomFont': require('./font.ttf')
});

loadAsync Method

Font.loadAsync(fontMap)
(fontMap: FontSource) => Promise<void>
Loads fonts asynchronously
import * as Font from 'expo-font';

await Font.loadAsync({
  'Inter': require('./Inter.ttf'),
  'Roboto': 'https://example.com/Roboto.ttf'
});
Font.isLoaded(fontFamily)
(fontFamily: string) => boolean
Checks if a font is loaded
if (Font.isLoaded('Inter-Black')) {
  console.log('Font is ready');
}
Font.isLoading(fontFamily)
(fontFamily: string) => boolean
Checks if a font is currently loading

FontSource Type

type FontSource = {
  [fontFamily: string]:
    | string              // URL or file path
    | number              // require() result
    | Asset               // expo-asset Asset
    | FontResource;       // Font resource object
};

Examples

Basic Font Loading

import { useFonts } from 'expo-font';
import { Text, View } from 'react-native';

function App() {
  const [fontsLoaded] = useFonts({
    'SpaceGrotesk-Bold': require('./assets/fonts/SpaceGrotesk-Bold.ttf'),
    'SpaceGrotesk-Regular': require('./assets/fonts/SpaceGrotesk-Regular.ttf'),
  });

  if (!fontsLoaded) {
    return null; // or <LoadingScreen />
  }

  return (
    <View>
      <Text style={{ fontFamily: 'SpaceGrotesk-Bold', fontSize: 24 }}>
        Bold Title
      </Text>
      <Text style={{ fontFamily: 'SpaceGrotesk-Regular', fontSize: 16 }}>
        Regular body text
      </Text>
    </View>
  );
}

With Splash Screen

import { useFonts } from 'expo-font';
import * as SplashScreen from 'expo-splash-screen';
import { useEffect } from 'react';

// Keep splash screen visible while loading
SplashScreen.preventAutoHideAsync();

function App() {
  const [fontsLoaded, fontError] = useFonts({
    'Inter-Black': require('./assets/fonts/Inter-Black.otf'),
    'Inter-Regular': require('./assets/fonts/Inter-Regular.otf'),
  });

  useEffect(() => {
    if (fontsLoaded || fontError) {
      // Hide splash screen after fonts load
      SplashScreen.hideAsync();
    }
  }, [fontsLoaded, fontError]);

  if (!fontsLoaded && !fontError) {
    return null;
  }

  return <YourApp />;
}

Load Remote Fonts

import * as Font from 'expo-font';

await Font.loadAsync({
  'Roboto': 'https://fonts.gstatic.com/s/roboto/v27/KFOmCnqEu92Fr1Mu4mxK.woff2',
  'OpenSans': 'https://fonts.gstatic.com/s/opensans/v27/mem8YaGs126MiZpBA.woff2'
});

Multiple Font Weights

import { useFonts } from 'expo-font';
import { Text, View } from 'react-native';

function App() {
  const [fontsLoaded] = useFonts({
    'Inter-Thin': require('./assets/fonts/Inter-Thin.ttf'),
    'Inter-Light': require('./assets/fonts/Inter-Light.ttf'),
    'Inter-Regular': require('./assets/fonts/Inter-Regular.ttf'),
    'Inter-Medium': require('./assets/fonts/Inter-Medium.ttf'),
    'Inter-Bold': require('./assets/fonts/Inter-Bold.ttf'),
    'Inter-Black': require('./assets/fonts/Inter-Black.ttf'),
  });

  if (!fontsLoaded) return null;

  return (
    <View>
      <Text style={{ fontFamily: 'Inter-Thin' }}>Thin</Text>
      <Text style={{ fontFamily: 'Inter-Light' }}>Light</Text>
      <Text style={{ fontFamily: 'Inter-Regular' }}>Regular</Text>
      <Text style={{ fontFamily: 'Inter-Medium' }}>Medium</Text>
      <Text style={{ fontFamily: 'Inter-Bold' }}>Bold</Text>
      <Text style={{ fontFamily: 'Inter-Black' }}>Black</Text>
    </View>
  );
}

Error Handling

import { useFonts } from 'expo-font';
import { Text, View } from 'react-native';

function App() {
  const [fontsLoaded, fontError] = useFonts({
    'CustomFont': require('./assets/fonts/CustomFont.ttf'),
  });

  if (fontError) {
    return (
      <View>
        <Text>Error loading fonts: {fontError.message}</Text>
      </View>
    );
  }

  if (!fontsLoaded) {
    return <Text>Loading fonts...</Text>;
  }

  return <Text style={{ fontFamily: 'CustomFont' }}>Hello</Text>;
}

Programmatic Loading

import * as Font from 'expo-font';

async function loadFontsAsync() {
  try {
    await Font.loadAsync({
      'Montserrat-Regular': require('./fonts/Montserrat-Regular.ttf'),
      'Montserrat-Bold': require('./fonts/Montserrat-Bold.ttf'),
    });
    console.log('Fonts loaded successfully');
  } catch (error) {
    console.error('Error loading fonts:', error);
  }
}

// Use in app initialization
loadFontsAsync();

Check Font Status

import * as Font from 'expo-font';

// Load font
await Font.loadAsync({
  'CustomFont': require('./font.ttf')
});

// Check if loaded
if (Font.isLoaded('CustomFont')) {
  console.log('Font is ready to use');
}

// Check if loading
if (Font.isLoading('CustomFont')) {
  console.log('Font is still loading');
}

Google Fonts Integration

Use with @expo-google-fonts:
npx expo install @expo-google-fonts/inter expo-font
import { useFonts, Inter_400Regular, Inter_700Bold } from '@expo-google-fonts/inter';
import { Text } from 'react-native';

function App() {
  const [fontsLoaded] = useFonts({
    Inter_400Regular,
    Inter_700Bold,
  });

  if (!fontsLoaded) return null;

  return (
    <>
      <Text style={{ fontFamily: 'Inter_400Regular' }}>Regular</Text>
      <Text style={{ fontFamily: 'Inter_700Bold' }}>Bold</Text>
    </>
  );
}

Custom Font Hook

import { useFonts } from 'expo-font';
import { useEffect, useState } from 'react';

function useFontLoader() {
  const [loaded, error] = useFonts({
    'AppFont-Regular': require('./assets/fonts/Regular.ttf'),
    'AppFont-Bold': require('./assets/fonts/Bold.ttf'),
  });
  
  const [ready, setReady] = useState(false);

  useEffect(() => {
    if (loaded || error) {
      setReady(true);
    }
  }, [loaded, error]);

  return { ready, error };
}

// Usage
function App() {
  const { ready, error } = useFontLoader();
  
  if (error) return <ErrorScreen error={error} />;
  if (!ready) return <LoadingScreen />;
  
  return <MainApp />;
}

Config Plugin

Embed fonts in native projects: app.json:
{
  "expo": {
    "plugins": [
      [
        "expo-font",
        {
          "fonts": [
            "./assets/fonts/Inter-Regular.ttf",
            "./assets/fonts/Inter-Bold.ttf"
          ]
        }
      ]
    ]
  }
}
Then rebuild:
npx expo prebuild

TypeScript

import { useFonts } from 'expo-font';
import type { FontSource } from 'expo-font';

const fonts: FontSource = {
  'MyFont': require('./font.ttf')
};

const [loaded, error]: [boolean, Error | null] = useFonts(fonts);

Platform Support

PlatformSupportedFont Formats
iOSTTF, OTF
AndroidTTF, OTF
WebTTF, OTF, WOFF, WOFF2

Best Practices

  1. Keep Splash Screen: Use expo-splash-screen while fonts load
  2. Subset Fonts: Only include needed characters to reduce file size
  3. Preload Critical Fonts: Load essential fonts during app initialization
  4. Handle Errors: Always check for font loading errors
  5. Use Font Weights: Load separate files for different weights rather than relying on synthetic bold

Resources