or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

font-loading.mdfont-utils.mdindex.mdreact-hooks.mdserver-side.md
tile.json

react-hooks.mddocs/

React Hooks

React hooks for integrating font loading into component lifecycles with automatic state management, error handling, and rehydration support for server-side rendering.

Capabilities

useFonts Hook

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>;
}

Error Handling Patterns

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>;
}

Advanced Usage Patterns

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>;
}

Hook Behavior

Loading State Management

  • Initial State: [false, null] on first render
  • Loading: [false, null] while fonts are being loaded
  • Success: [true, null] when all fonts have loaded successfully
  • Error: [false, Error] if any font fails to load

Rehydration Support

The 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

Caching and Performance

  • Deduplication: Multiple components using the same font map share the same loading promise
  • Caching: Successfully loaded fonts are cached and reused across component instances
  • Cleanup: Hook automatically handles cleanup when components unmount

Font Map Stability

// ❌ 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);
}

Platform Considerations

  • Static vs Runtime: Hook uses different loading strategies on server vs client
  • Web Rehydration: Optimized to prevent layout shifts during SSR rehydration
  • Native Performance: Leverages platform-specific font caching mechanisms
  • Error Recovery: Graceful fallback to system fonts when custom fonts fail to load