javascripttypescriptnext.jsgoogle-fontstamagui

How to load fonts in Next.js Tamagui Monorepo


So I followed the documentation on how to load fonts in Tamagui: Configuration and I was able to load my fonts on expo.

apps/expo/app/_layout.tsx:

// ...
import { Glegoo_400Regular, Glegoo_700Bold } from '@expo-google-fonts/glegoo'
import {
  OpenSans_400Regular,
  OpenSans_600SemiBold,
  OpenSans_700Bold,
} from '@expo-google-fonts/open-sans'
import { Nunito_400Regular, Nunito_700Bold } from '@expo-google-fonts/nunito'

export const unstable_settings = {
  // Ensure that reloading on `/user` keeps a back button present.
  initialRouteName: 'Home',
}

// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync()

export default function App() {
  const [interLoaded, interError] = useFonts({
    Inter: require('@tamagui/font-inter/otf/Inter-Medium.otf'),
    InterBold: require('@tamagui/font-inter/otf/Inter-Bold.otf'),
    Glegoo: Glegoo_400Regular,
    GlegooBold: Glegoo_700Bold,
    OpenSans: OpenSans_400Regular,
    OpenSansSemiBold: OpenSans_600SemiBold,
    OpenSansBold: OpenSans_700Bold,
    Nunito: Nunito_400Regular,
    NunitoBold: Nunito_700Bold,
  })

  useEffect(() => {
    if (interLoaded || interError) {
      // Hide the splash screen after the fonts have loaded (or an error was returned) and the UI is ready.
      SplashScreen.hideAsync()
    }
  }, [interLoaded, interError])

  if (!interLoaded && !interError) {
    return null
  }

  return <RootLayoutNav />
}

// ...
}

I used Open Sans for font body in my Tamagui config and the changes were reflected on native but not on web.

packages/config/src/tamagui.config.ts:

// ...

// Expo Google Fonts for branding.
const glegooFont = createFont({
  family: 'Glegoo',
  size: {
    1: 12,
    2: 14,
    3: 16,
    4: 18,
    5: 20,
    6: 22,
  },
  weight: {
    3: '700',
  },
  face: {
    700: { normal: 'GlegooBold' },
  },
})

const openSansFont = createFont({
  family: 'OpenSans',
  size: {
    1: 12,
    2: 14,
    3: 16,
    4: 18,
    5: 20,
    6: 22,
  },
  weight: {
    2: '600',
    3: '700',
  },
  face: {
    600: { normal: 'OpenSansSemiBold' },
    700: { normal: 'OpenSansBold' },
  },
})

const nunitoFont = createFont({
  family: 'Nunito',
  size: {
    1: 12,
    2: 14,
    3: 16,
    4: 18,
    5: 20,
    6: 22,
  },
  weight: {
    3: '700',
  },
  face: {
    700: { normal: 'NunitoBold' },
  },
})

// ...

export const config = createTamagui({
  defaultFont: 'body',
  animations,
  shouldAddPrefersColorThemes: true,
  themeClassNameOnRoot: true,

  // ...
 // I used Open Sans for body
  fonts: {
    body: openSansFont, 
    heading: headingFont,
  },
  // ...

The fonts that I loaded are not being shown on the web because I haven't successfully loaded them yet on the next.js part of the mono repo but I was wondering what is the most appropriate method to load them, as the docs: Configuration haven't really gone into details on how I should do it.

I tried this based on the docs but it still doesn't apply my font configs from my tamagui.config.ts in the web. I also tried using next/google/font method to load fonts but I still haven't been able to make it work. What additional steps should I do to make sure that my fonts are loaded in Next.js?

app/next/pages/_app.tsx:

function MyApp({ Component, pageProps }: SolitoAppProps) {
  return (
    <>
      <Head>
        <title>Tamagui • Pages Router</title>
        <meta
          name="description"
          content="Tamagui, Solito, Expo & Next.js"
        />
        <link
          rel="icon"
          href="/favicon.ico"
        />
        {/* I tried loading fonts like this in Next.js */}
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
        <link
          href="https://fonts.googleapis.com/css2?family=Glegoo:wght@400;700&family=Nunito:ital,wght@0,200..1000;1,200..1000&family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap"
          rel="stylesheet"
        />
        <script
          dangerouslySetInnerHTML={{
            // avoid flash of animated things on enter:
            __html: `document.documentElement.classList.add('t_unmounted')`,
          }}
        />
      </Head>
      <ThemeProvider>
        <Component {...pageProps} />
      </ThemeProvider>
    </>
  )
}

Solution

  • I was able to finally fix it by doing these steps:

    1. Create a CSS file named _app.styles.css in the apps/next/pages directory.
    2. Import the url link provided by Google fonts in that file:
    /* apps/next/pages/_app.styles.css */
    @import url('https://fonts.googleapis.com/css2?family=Glegoo:wght@400;700&family=Nunito:ital,wght@0,200..1000;1,200..1000&family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap');
    
    1. Import the CSS file into _app.tsx file located in the same directory:
    // apps/next/pages/_app.tsx
    import '@tamagui/core/reset.css'
    import '@tamagui/font-inter/css/400.css'
    import '@tamagui/font-inter/css/700.css'
    // import it here
    import './_app.styles.css'
    import 'raf/polyfill'
    
    // ...
    
    function MyApp({ Component, pageProps }: SolitoAppProps) {
      return (
        <>
          <Head>
            <title>Tamagui • Pages Router</title>
    
    
    1. Finally, define the font families within the tamagui.config.ts:
    // packages/config/src/tamagui.config.ts
    // Define loaded fonts using createFont
    const glegooFont = createFont({
      family: 'Glegoo, serif',
      size: {
        1: 12,
        2: 14,
        3: 16,
        4: 18,
        5: 20,
        6: 22,
      },
      weight: {
        1: '400',
        3: '700',
      },
    
      // for native only, alternate family based on weight/style
      // make sure to match the family names to the ones imported in expo
      face: {
        400: { normal: 'Glegoo' },
        700: { normal: 'GlegooBold' },
      },
    })
    
    const openSansFont = createFont({
      // this ensures that the family name format matches the one
      // declared in expo to prevent warnings when running in native
      family: isWeb ? 'Open Sans, serif' : 'OpenSans',
      size: {
        1: 12,
        2: 14,
        3: 16,
        4: 18,
        5: 20,
        6: 22,
      },
      weight: {
        1: '400',
        2: '600',
        3: '700',
      },
    
      // for native only, alternate family based on weight/style
      // make sure to match the family names to the ones imported in expo
      face: {
        400: { normal: 'OpenSans' },
        600: { normal: 'OpenSansSemiBold' },
        700: { normal: 'OpenSansBold' },
      },
    })
    // ... 
    export const config = createTamagui({
      defaultFont: 'body',
      animations,
      // ...
    
      fonts: {
        // used Open Sans as the default body font for branding
        // you could revert to using Tamagui's sample with bodyFont.
        body: openSansFont,
        heading: headingFont,
        glegoo: glegooFont,
        openSans: openSansFont,
        nunito: nunitoFont,
      },
    

    Notice how I checked if I was running in web using isWeb when defining Open Sans because the family format I used didn't include any spaces when I declared them in expo, this prevents warnings when running in native and it was actually mentioned in the docs: Troubleshooting. Hopefully they'll update their docs in the near future to show a more detailed guide on how to load fonts.