React hooks for integrating font loading into component lifecycles with automatic state management, error handling, and rehydration support for server-side rendering.
Load fonts and track their loading state within React components.
/**
* Load a map of fonts at runtime and return loading state and error
* @param map - Font family name or map of fontFamily to FontSource
* @returns Tuple of [loaded: boolean, error: Error | null]
*/
function useFonts(
map: string | Record<string, FontSource>
): [boolean, Error | null];Usage Examples:
import React from 'react';
import { Text, View } from 'react-native';
import { useFonts } from 'expo-font';
// Basic usage with multiple fonts
function MyComponent() {
const [fontsLoaded, error] = useFonts({
'Inter-Regular': require('./assets/fonts/Inter-Regular.otf'),
'Inter-Bold': require('./assets/fonts/Inter-Bold.otf'),
'CustomFont': 'https://example.com/font.woff2',
});
if (error) {
// Handle font loading error
console.error('Font loading failed:', error);
throw error; // or show error UI
}
if (!fontsLoaded) {
// Show loading state while fonts are loading
return <LoadingScreen />;
}
// Fonts are loaded, safe to use
return (
<View>
<Text style={{ fontFamily: 'Inter-Regular' }}>
Regular text with custom font
</Text>
<Text style={{ fontFamily: 'Inter-Bold' }}>
Bold text with custom font
</Text>
</View>
);
}
// Usage with single font (string)
function SingleFontComponent() {
const [fontLoaded, error] = useFonts('MyCustomFont');
if (error) throw error;
if (!fontLoaded) return <LoadingSpinner />;
return <Text style={{ fontFamily: 'MyCustomFont' }}>Hello!</Text>;
}
// Usage with FontResource options
function WebOptimizedComponent() {
const [fontsLoaded, error] = useFonts({
'WebFont': {
uri: require('./fonts/WebFont.woff2'),
display: FontDisplay.SWAP // Improves web loading experience
}
});
if (error) throw error;
if (!fontsLoaded) return null;
return <Text style={{ fontFamily: 'WebFont' }}>Optimized for web</Text>;
}The hook provides comprehensive error handling for various failure scenarios:
import { useFonts } from 'expo-font';
function ComponentWithErrorHandling() {
const [fontsLoaded, error] = useFonts({
'MyFont': require('./assets/fonts/MyFont.ttf'),
});
// Handle different error types
if (error) {
if (error.message.includes('network')) {
// Network error - show retry option
return <RetryableFontError onRetry={() => window.location.reload()} />;
} else {
// Other errors - fall back to system fonts
console.warn('Font loading failed, using system fonts:', error);
return <Text style={{ fontFamily: 'System' }}>Fallback content</Text>;
}
}
if (!fontsLoaded) {
return <LoadingSpinner />;
}
return <Text style={{ fontFamily: 'MyFont' }}>Custom font content</Text>;
}import React, { useMemo } from 'react';
import { useFonts } from 'expo-font';
// Conditional font loading based on user preferences
function AdaptiveFontComponent({ userPreferredFont }) {
const fontMap = useMemo(() => {
const fonts = {
'DefaultFont': require('./fonts/Default.ttf'),
};
if (userPreferredFont === 'dyslexia-friendly') {
fonts['DyslexiaFont'] = require('./fonts/OpenDyslexic.ttf');
}
return fonts;
}, [userPreferredFont]);
const [fontsLoaded, error] = useFonts(fontMap);
if (error) throw error;
if (!fontsLoaded) return <LoadingScreen />;
const fontFamily = userPreferredFont === 'dyslexia-friendly'
? 'DyslexiaFont'
: 'DefaultFont';
return (
<Text style={{ fontFamily }}>
Adaptive font content
</Text>
);
}
// Using with React Suspense
function SuspenseFontComponent() {
const [fontsLoaded, error] = useFonts({
'MyFont': require('./fonts/MyFont.ttf'),
});
if (error) throw error;
// Throw promise for Suspense boundary
if (!fontsLoaded) {
throw new Promise(resolve => {
// This pattern works with the loading behavior
const checkLoaded = () => {
if (fontsLoaded) resolve();
else setTimeout(checkLoaded, 100);
};
checkLoaded();
});
}
return <Text style={{ fontFamily: 'MyFont' }}>Suspense content</Text>;
}[false, null] on first render[false, null] while fonts are being loaded[true, null] when all fonts have loaded successfully[false, Error] if any font fails to loadThe hook optimizes for server-side rendering and client-side rehydration:
// On server: fonts are registered statically, hook returns [true, null]
// On client: hook checks if fonts are already loaded before starting load process
// This prevents flash of unstyled text (FOUT) during rehydration// ❌ Don't create new objects on every render
function BadComponent() {
const [fontsLoaded] = useFonts({
'MyFont': require('./MyFont.ttf') // New object every render
});
// This will cause unnecessary re-loading
}
// ✅ Use stable references
const FONT_MAP = {
'MyFont': require('./MyFont.ttf')
};
function GoodComponent() {
const [fontsLoaded] = useFonts(FONT_MAP); // Stable reference
// Font loading is optimized
}
// ✅ Or use useMemo for dynamic fonts
function DynamicComponent({ theme }) {
const fontMap = useMemo(() => ({
'ThemeFont': require(`./fonts/${theme}.ttf`)
}), [theme]);
const [fontsLoaded] = useFonts(fontMap);
}