iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🦔

Using Cloudflare Fonts with React

に公開

Code Repository

https://github.com/ReactLibraries/cloudflare-fonts

What is Cloudflare Fonts?

Cloudflare Fonts reads Google Font link tags in the HTML, converts them to style tags, and inlines the font image addresses. Additionally, since the images are linked via Cloudflare's high-speed CDN (Content Delivery Network), page loads become faster.

However, this feature is incompatible with React SSR because it rewrites the tags in the output HTML. This causes inconsistencies during hydration on the client side, making the entire thing subject to re-rendering, which leads to flickering and decreased performance.

How to Prevent Inconsistencies

Cloudflare Fonts removes the Google Font link tags in the head tag and inserts style tags. The hydration error occurs because the link tag disappears and cannot be mounted. As for the added style tags, even if there's nothing to assign on the virtual DOM side, they are simply skipped without causing errors. In other words, the issue is solved if only the link tags are removed from the client-side virtual DOM.

Creating the Implementation Code

We will create a React component to control font loading. This component generates link tags normally on the server side and removes them on the client side. However, to ensure that issues do not occur in environments like local setups where Cloudflare Fonts might not be enabled, it checks the content of the style tags to verify whether the conversion has been performed.

import React from "react";
import { useRef, type FC } from "react";

type FontProperty = {
  isLoaded?: boolean;
  isData?: boolean;
};

const isServer = typeof window === "undefined";

export const CloudflareFonts: FC<{ href: string | string[] }> = ({ href }) => {
  const property = useRef<FontProperty>({}).current;
  if (!property.isLoaded && !isServer) {
    property.isLoaded = true;
    const nodes = document.querySelectorAll("head style[type='text/css']");
    property.isData = Array.from(nodes).some((v) =>
      v.textContent?.includes("url(/cf-fonts/")
    );
  }
  if (!property.isData) {
    const urls = Array.isArray(href) ? href : [href];
    return (
      <>
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link
          rel="preconnect"
          href="https://fonts.gstatic.com"
          crossOrigin="anonymous"
        />
        {urls.map((href) => (
          <link key={href} rel="stylesheet" href={href} />
        ))}
      </>
    );
  }
  return null;
};

How to Use

Here is an excerpt when using it with React Router. You simply specify the font addresses in the CloudflareFonts component.
Note that to enable this, you need to enable Cloudflare Fonts in the custom domain settings on the Cloudflare side.

import { CloudflareFonts } from "cloudflare-fonts";

export function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
        <Links />
        <CloudflareFonts
          href={[
            "https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@100..900&display=swap",
            "https://fonts.googleapis.com/css2?family=Kaisei+Decol&display=swap",
            "https://fonts.googleapis.com/css2?family=Dela+Gothic+One&display=swap",
          ]}
        />
      </head>
      <body>
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

Execution Results

When checking the elements in the browser, you can see that the font data has been correctly converted and remains expanded.

Summary

This method is effective when you want to use Web Fonts while speeding up the site display as much as possible.

GitHubで編集を提案

Discussion