DEV Community

Adam Butterfield
Adam Butterfield

Posted on

Making Font Loading More Efficient with React Content Font

Language:

When it comes to web performance, every kilobyte matters. Developers continuously strive to make applications more efficient, ensuring a smooth experience for users, regardless of their internet speed. This is particularly true when developing applications in languages with vast character sets such as Japanese or Chinese. Ensuring efficient use of fonts in these scenarios can be a major concern due to the typically large size of the fonts.

One of the challenges is dealing with languages like Japanese that require large font files. For example, a single font weight for Noto Sans Japanese is a whopping 5.7 MB. It's not ideal to have your users download such a big file, not to mention if you need more than one font weight.

Enter React Content Font, a package developed specifically to tackle this issue.

What is React Content Font?

React Content Font is a package designed for React applications. It works by checking a webpage, getting a list of unique characters on that page, and then requesting a font from Google Fonts with only those characters included. This process uses an optimized request, ensuring that only the required characters are loaded, minimizing the data your users need to download.

The package is easy to use, as simple as adding the context provider high up in your application. Simply provide the font you want with the fontName prop, and it will by default request a normal (meaning 400) weight font. 

For example, if you have a Next.js app using App Router, you can update your app/layout.tsx like this:

import FontProvider from 'react-content-font';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="jp">
      <body>
        <FontProvider fontName="Noto Sans JP">{children}</FontProvider>
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

If you need more than one font weight, React Content Font got you covered with the fontWeights prop.

import FontProvider from 'react-content-font';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="jp">
      <body>
        <FontProvider fontName="Noto Sans JP" fontWeights={[400, 600]}>
          {children}
        </FontProvider>
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

Meeting Diverse Font Needs

You may wonder if there are any Japanese fonts with italic variants. While it's uncommon, maybe the font you want to use does have this feature. React Content Font allows you to request italic variants for whatever weight you desire.

import FontProvider from 'react-content-font';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="jp">
      <body>
        <FontProvider
          fontName="Noto Sans JP"
          fontWeights={[400, 600, ['ital', 400], ['ital', 900]]}
        >
          {children}
        </FontProvider>
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

Moreover, the package allows you to specify the font-display property. This property defines how font files are loaded and rendered by the browser, which directly impacts the perceived performance.

import FontProvider from 'react-content-font';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="jp">
      <body>
        <FontProvider fontName="Noto Sans JP" display="block">
          {children}
        </FontProvider>
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

Checking Font Load State

In some cases, you might want to wait for the font to load before showing content, or display a loading state. React Content Font provides a hook that flags the load state of the font. This feature allows you to control the visibility of your content based on the font's load state.

'use client';

import { useFontContext } from 'react-content-font';

export default function PageText() {
  const { isFontLoaded } = useFontContext();

  return (
    <p style={{ visibility: isFontLoaded ? 'visible' : 'hidden' }}>
      よそはほかまあこの威圧心というのの後をしないう。きっと場合で仕事帰りはひょろひょろその評でたなりでするが行くたをも表裏できなけれでば、なぜにはもっなないうた。個人にできたのはついに十月から向後ますだない。もっと岡田さんから批評その道それほど説明が云った他人その自力いつか修養にというお吹聴だでますでて、この先刻は私か同人引込で思うば、大森さんののを自分の私に勢いごろかと広めよば私手でご話の出ように引続きお[#「に解らうだので、とにかくたとい指図にするだろといるです事を考えだう。
    </p>
  );
}
Enter fullscreen mode Exit fullscreen mode

It's crucial, however, to render the text so the characters can be discovered and included in the requested font.

Detecting Font Updates

React Content Font also allows you to know if the font is being updated, offering another layer of control over your application's UX.

'use client';

import { useFontContext } from 'react-content-font';

export default function PageText() {
  const { isFontLoaded } = useFontContext();

  return (
    <>
      {isFontLoaded && (
        <p>
          よそはほかまあこの威圧心というのの後をしないう。きっと場合で仕事帰りはひょろひょろその評でたなりでするが行くたをも表裏できなけれでば、なぜにはもっなないうた。個人にできたのはついに十月から向後ますだない。もっと岡田さんから批評その道それほど説明が云った他人その自力いつか修養にというお吹聴だでますでて、この先刻は私か同人引込で思うば、大森さんののを自分の私に勢いごろかと広めよば私手でご話の出ように引続きお[#「に解らうだので、とにかくたとい指図にするだろといるです事を考えだう。
        </p>
      )}
    </>
  )
}
Enter fullscreen mode Exit fullscreen mode

Again, just like with checking the load state, you must render the text for the characters to be discovered and included in the requested font.

Let me know what you think!

As the sole developer behind React Content Font, I've found the journey challenging yet rewarding. It's something I built for myself, but I'm pretty sure other people will want to use it too.

Have you found this package useful in your projects? Do you see any areas where it can be improved? Or perhaps, you have some creative ideas for future enhancements? I'd love to hear your thoughts!

Top comments (0)