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

server-side.mddocs/

Server-Side Support

Server-side rendering support with static font registration and resource management. Enables font loading in SSR environments and provides utilities for managing font resources across server contexts.

Capabilities

Static Font Registration

Register fonts for server-side rendering without asynchronous loading.

/**
 * Register a font for server-side rendering (synchronous operation)
 * @param fontFamily - Name to register the font under
 * @param source - Font source (asset, URI, or FontResource)
 */
function registerStaticFont(
  fontFamily: string, 
  source?: FontSource | null
): void;

Usage Examples:

import { registerStaticFont } from 'expo-font/build/server';

// Register fonts for SSR
registerStaticFont('Inter-Regular', require('./assets/fonts/Inter-Regular.otf'));
registerStaticFont('Inter-Bold', require('./assets/fonts/Inter-Bold.otf'));

// With FontResource options
registerStaticFont('WebFont', {
  uri: require('./assets/fonts/WebFont.woff2'),
  display: FontDisplay.SWAP
});

// Register multiple fonts for a component
function registerAppFonts() {
  const fonts = {
    'CustomFont1': require('./fonts/Font1.ttf'),
    'CustomFont2': require('./fonts/Font2.ttf'),
    'CustomFont3': { uri: './fonts/Font3.woff2', display: FontDisplay.OPTIONAL }
  };

  Object.entries(fonts).forEach(([name, source]) => {
    registerStaticFont(name, source);
  });
}

Server Resource Management

Manage font resources across server rendering contexts.

/**
 * Get server resources that should be statically extracted
 * @returns Array of resource identifiers for static extraction
 */
function getServerResources(): string[];

/**
 * Clear server resources from global scope (cleanup between renders)
 */
function resetServerContext(): void;

Usage Examples:

import { 
  getServerResources, 
  resetServerContext, 
  registerStaticFont 
} from 'expo-font/build/server';

// Server-side rendering setup
function renderAppWithFonts(req, res) {
  try {
    // Register fonts for this render
    registerStaticFont('AppFont', require('./fonts/AppFont.ttf'));
    
    // Get resources that need to be included
    const fontResources = getServerResources();
    console.log('Required font resources:', fontResources);
    
    // Render your app
    const html = renderToString(<App />);
    
    // Generate font CSS or preload links based on resources
    const fontLinks = fontResources.map(resource => 
      `<link rel="preload" href="${resource}" as="font" crossorigin>`
    ).join('\n');
    
    const fullHtml = `
      <html>
        <head>
          ${fontLinks}
        </head>
        <body>
          <div id="root">${html}</div>
        </body>
      </html>
    `;
    
    res.send(fullHtml);
  } finally {
    // Clean up for next render
    resetServerContext();
  }
}

// Next.js integration
export async function getServerSideProps(context) {
  // Register fonts needed for this page
  registerStaticFont('PageFont', require('./fonts/PageFont.ttf'));
  
  const resources = getServerResources();
  
  return {
    props: {
      fontResources: resources
    }
  };
}

Integration with useFonts Hook

The server-side functions work seamlessly with the useFonts hook for SSR:

import { useFonts } from 'expo-font';
import { registerStaticFont } from 'expo-font/build/server';

// Server-side: fonts are registered statically
if (typeof window === 'undefined') {
  registerStaticFont('MyFont', require('./MyFont.ttf'));
}

function MyComponent() {
  // On server: returns [true, null] immediately (fonts pre-registered)
  // On client: loads fonts asynchronously, returns loading state
  const [fontsLoaded, error] = useFonts({
    'MyFont': require('./MyFont.ttf')
  });

  if (error) throw error;
  if (!fontsLoaded) return <LoadingSpinner />;

  return <Text style={{ fontFamily: 'MyFont' }}>SSR-ready text</Text>;
}

Advanced SSR Patterns

import { 
  registerStaticFont, 
  getServerResources, 
  resetServerContext 
} from 'expo-font/build/server';

// Font preloading utility for SSR frameworks
class FontManager {
  private static registeredFonts = new Set<string>();

  static registerForSSR(fontMap: Record<string, FontSource>) {
    Object.entries(fontMap).forEach(([name, source]) => {
      if (!this.registeredFonts.has(name)) {
        registerStaticFont(name, source);
        this.registeredFonts.add(name);
      }
    });
  }

  static getPreloadLinks(): string[] {
    const resources = getServerResources();
    return resources.map(resource => 
      `<link rel="preload" href="${resource}" as="font" type="font/woff2" crossorigin>`
    );
  }

  static cleanup() {
    resetServerContext();
    this.registeredFonts.clear();
  }
}

// Use in your SSR setup
function renderWithFonts(Component, props) {
  // Register fonts used by component tree
  FontManager.registerForSSR({
    'Inter': require('./fonts/Inter.woff2'),
    'Roboto': require('./fonts/Roboto.woff2')
  });

  const html = renderToString(<Component {...props} />);
  const preloadLinks = FontManager.getPreloadLinks();
  
  FontManager.cleanup();

  return { html, preloadLinks };
}

// Critical font loading for performance
function registerCriticalFonts() {
  // Register only fonts needed for above-the-fold content
  registerStaticFont('PrimaryFont', {
    uri: require('./fonts/Primary.woff2'),
    display: FontDisplay.BLOCK // Ensure no FOUT for critical text
  });
}

// Deferred font loading for non-critical content
function registerDeferredFonts() {
  registerStaticFont('SecondaryFont', {
    uri: require('./fonts/Secondary.woff2'),
    display: FontDisplay.SWAP // Allow FOUT for non-critical text
  });
}

Error Handling

import { registerStaticFont } from 'expo-font/build/server';

// Handle registration errors
function safeRegisterFont(name: string, source: FontSource) {
  try {
    registerStaticFont(name, source);
    console.log(`Font registered: ${name}`);
  } catch (error) {
    if (error.code === 'ERR_FONT_SOURCE') {
      console.warn(`Invalid font source for ${name}:`, source);
    } else {
      console.error(`Failed to register font ${name}:`, error);
    }
  }
}

// Validate font sources before registration
function registerValidatedFonts(fontMap: Record<string, FontSource>) {
  Object.entries(fontMap).forEach(([name, source]) => {
    if (source == null) {
      console.warn(`Skipping ${name}: null/undefined source`);
      return;
    }
    
    safeRegisterFont(name, source);
  });
}

Platform and Environment Considerations

Server vs Client Behavior

  • Server: Fonts are registered synchronously, no async loading
  • Client: Falls back to normal async loading behavior
  • Hydration: Client checks existing fonts before loading to prevent FOUT

Framework Integration

  • Next.js: Use in getServerSideProps or getStaticProps
  • Gatsby: Use in gatsby-ssr.js for static generation
  • Custom SSR: Integrate in your server rendering pipeline

Performance Optimization

  • Register only critical fonts during SSR
  • Use FontDisplay.BLOCK for critical text, SWAP for non-critical
  • Preload font resources in HTML head
  • Clean up server context between renders to prevent memory leaks

Caveats and Limitations

  • Server registration is synchronous - no loading states
  • Font files must be available at build time for SSR
  • Some font features (like variable fonts) may have limited SSR support
  • Memory usage scales with number of registered fonts per render