vercel / next.js

The React Framework
https://nextjs.org
MIT License
125.28k stars 26.77k forks source link

Font preload not working as expected #62332

Open jasonthorsness opened 7 months ago

jasonthorsness commented 7 months ago

Link to the code that reproduces this issue

https://github.com/jasonthorsness/repro001

To Reproduce

npm run build npm run start Navigate to localhost:3000 in dev tools in Chrome and observe the absence of a preload for the fonts in the initial HTML response

Current vs. Expected behavior

Expected Both the Google and Local fonts should have a link rel preload in the first HTML sent back from the server

Actual Neither font has a preload entry in the first HTML sent back from the server and they aren't downloaded until the CSS references them.

Provide environment information

Operating System:
  Platform: win32
  Arch: x64
  Version: Windows 11 Pro
  Available memory (MB): 29438
  Available CPU cores: 16
Binaries:
  Node: 20.11.1
  npm: N/A
  Yarn: N/A
  pnpm: N/A
Relevant Packages:
  next: 14.1.1-canary.67 // Latest available version is detected (14.1.1-canary.67).
  eslint-config-next: 14.1.0
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.3.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

App Router, Font optimization (next/font)

Which stage(s) are affected? (Select all that apply)

next dev (local), next build (local), next start (local)

Additional context

Here is the content I see in the initial HTML response - note lack of preload for the fonts

    <head>
        <meta charSet="utf-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1"/>
        <link rel="preload" as="image" href="/vercel.svg" fetchPriority="high"/>
        <link rel="preload" as="image" href="/next.svg" fetchPriority="high"/>
        <link rel="stylesheet" href="/_next/static/css/be383e181a203198.css" crossorigin="" data-precedence="next"/>
        <link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-43737e769a979b7d.js" crossorigin=""/>
        <script src="/_next/static/chunks/fd9d1056-9bd6272302a380c1.js" async="" crossorigin=""></script>
        <script src="/_next/static/chunks/54-f0a3df5edf4d335b.js" async="" crossorigin=""></script>
        <script src="/_next/static/chunks/main-app-cd149db4694c0f16.js" async="" crossorigin=""></script>
        <script src="/_next/static/chunks/407-a5a598ccc34bf6cf.js" async=""></script>
        <script src="/_next/static/chunks/app/page-339d9c829db39b7a.js" async=""></script>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app"/>
        <link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="16x16"/>
        <script src="/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js" crossorigin="" noModule=""></script>
    </head>
jasonthorsness commented 7 months ago

My workaround for this in my own application is to place this in in layout.tsx. This performs the preload as expected but I have to deal with the mangled font name; which I am hoping is a content hash.

        <link
          rel="preload"
          href="/_next/static/media/bd734242e06bd6ad-s.p.woff2"
          fetchPriority="high"
          as="font"
          type="font/woff2"
          crossOrigin="anonymous"
        />
jasonthorsness commented 7 months ago

I can confirm that this does NOT affect the site as deployed on Vercel - just local next build/next start.

vmeylan commented 7 months ago

Hi @jasonthorsness I have the exact same error which I have been debugging for the past few days, without success. All of the attempts have done were all deployed on Vercel and not local.

We both import Inter fonts from next/font/google. import { Inter } from "next/font/google";

I did a Vercel build old of 12 days where fonts and markdown are displayed correctly: https://app-b8ke3b5tj-mevfyis-projects.vercel.app/sign-in?callbackUrl=https%3A%2F%2Fapp-b8ke3b5tj-mevfyis-projects.vercel.app%2Fsign-in

but it is not the case on the next build that was 3 days ago https://app-i8tqpo8qn-mevfyis-projects.vercel.app/sign-in?callbackUrl=https%3A%2F%2Fapp-i8tqpo8qn-mevfyis-projects.vercel.app%2Fsign-in

There is an HTML header difference between the old fonts where it worked, where the header has this:

<link rel="preload" as="font" href="/_next/static/media/86fdec36ddd9097e-s.p.woff2" crossorigin="" type="font/woff2">
<link rel="preload" as="font" href="/_next/static/media/c9a5bc6a7c948fb0-s.p.woff2" crossorigin="" type="font/woff2">
<link rel="stylesheet" href="/_next/static/css/c7298abc4b596cfd.css" data-precedence="next">

while the latest one only has this:

<link rel="stylesheet" href="/_next/static/css/2e0492792cf9d089.css" data-precedence="next">

I did the following:

Other symptoms: In the correct app build (left), in Sources tab, I have _next/static/media with the .woff2 items, while in the app where fonts are not preloaded (right), I do not have this media tab in _next/static: image

vmeylan commented 7 months ago

TEMPORARY FIX: reverting back to a previous Vercel CLI version 33.5.1 -> 33.5.0 worked. I added back a layout.tsx change I had made (which was a fix attempt which had no effect) and kept the Vercel CLI version revert (since it is defined as an environment variable).

To set the Vercel CLI version, add the env variable VERCEL_CLI_VERSION and set it to vercel@33.5.0.

Now the exact reason on why fonts are not preloaded on vercel@33.5.1 (as well as vercel@33.5.2 ) is to be determined

phacks commented 7 months ago

I can confirm that @vmeylan's suggestion of setting the VERCEL_CLI_VERSION to vercel@33.5.0 did fix my production issue of the Inter font not being set in production (even though I followed the docs here: https://nextjs.org/docs/app/api-reference/components/font#css-variables). Thanks for sharing!

nic-vo commented 5 months ago

@jasonthorsness @vmeylan Can also confirm this is issue. According to the network tab in Chrome dev tools, the browser only requests fonts once it parses the global stylesheet that contains the Next.js-generated @font-face rules. Inspecting the initial HTML document reveals that preload <link> tags for the desired fonts are missing, as you describe.

In my case (next@14.1.14, vercel_cli@latest), Next.js seems to include preload <link> tags for any fallback fonts it generates, but not for the actual desired fonts themselves.

My workaround is setting preload: false for fonts because the effect on layout shift in my use case is ~0.01 according to Lighthouse, but preloading these auto-generated fallback fonts increases LCP by almost 1 second

next_font_network

s-h-a-d-o-w commented 2 days ago

I faced the same problem today and am somewhat confident that it's Windows-specific. Because in addition to vercel obviously building on Linux, I also just built the same project using WSL and got the preload links as well.

(I'm not using the vercel CLI and wonder whether people having problems with that are actually facing a different problem. Or maybe some transitive dependency is different with different CLI versions that also happens to be different on different OSes? Something native? 🤔)