next.js: Unexpected onLoad attribute behaviour in next/head

Bug report

Describe the bug

In my project raam I’m tying set "Inter" as my font by linking to a stylesheet from Google Fonts.

In Head (a component that extends next/head) I’m using:

<link rel="preconnect" href="https://fonts.gstatic.com/" crossOrigin="" />
<link
  href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap"
  rel="stylesheet"
/>

(note the preconnect to the actual font assets, then the display=swap which is intended to show the fallback font until the loaded font is swapped in)

When I then go to view the page (in Firefox particularly) I see a flash of unstyled/invisible text: https://raam-git-chore-inter.joebell.now.sh/

Screenshots

ezgif-2-8ea6320f12f1

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Go to https://raam-git-chore-inter.joebell.now.sh/ (in Firefox where this is more visible)
  2. Hard reload and watch the behaviour of fonts

Expected behavior

The system font is seen first, and ‘swapped’ to Inter on load.

System information

  • OS: macOS
  • Browser (if applies): Firefox 75
  • Version of Next.js: 9.3.3
  • Version of Node.js: 13.8.0

Additional context

I appreciate there’s a lot of variables here: Theme UI, Google Fonts, Now.sh, Next.js, but it would be good if we can figure out the root cause here for others using Next.js. If it’s something that could be resolved by adding some documentation I’d be more than happy to do so.

Thanks again for all of the team’s hard work on Next.js, it’s a real pleasure to work with

Prior reading

Example

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 14
  • Comments: 15 (9 by maintainers)

Commits related to this issue

Most upvoted comments

Following seems to work

<style dangerouslySetInnerHTML={{
  __html: `</style>
    <link
      rel="stylesheet"
      href="https://...."
      media="print"
      onload="this.media = 'all';"
    />
    <style>`
}}></style>

As an update I’ve tested this new approach, which is a mix of font-loading performance suggestions I’ve found online:

<link
  rel="preload"
  href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap"
  as="style"
/>
<link
  href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap"
  rel="stylesheet"
  media="print"
  // // Next.js doesn't like this but it allows us to load CSS asynchronously
  // @ts-ignore
  onLoad="this.media='all'"
/>
Before After
Screenshot 2020-05-17 at 13 40 49 Screenshot 2020-05-17 at 13 58 06

However a slight flash still remains and TypeScript doesn’t like my onLoad="this.media='all'" as it doesn’t accept string values. Am I doing this wrong or do I need to raise a PR to update types?

I have a hunch that something in next/head is changing after load on the client, but I’m not experienced enough to know what’s happening under the hood to cause this FOUT.


Update #1 - I’ve tried fg-loadCSS but the same FOUT issue occurs. Whatever approach there is still a small FOUT.

Copied from #16065 where I have a problem related to what OP is trying to do. If I want to use

<link rel="preload" href="https://..." as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="https://...></noscript>

to make loading CSS async, that does not work because I’d have to rework that to using onLoad with a function, but that function is never executed. So

  1. Is there a way to just insert the above HTML as-is, without having to comply to JSX?
  2. If not, is there a way to make onLoad work?
  3. If not, is there another easy way to make external CSS load async?

Thanks for getting back to me @timneutkens, I was just about to update this issue as I’ve managed to build a workaround solution in the meantime: next-google-fonts

I’d still prefer to use onload, but it seems fine for now

Could someone add a reproduction that I could clone and test with the newest version of Next.js? (as of writing 12.0.8)? I only see a deployed version that is more than a year old.

I’m happy to investigate further then.

After some further investigation with @csswizardry, we found that when JS is disabled the onload attribute is missing entirely:

Screenshot 2020-05-18 at 16 47 29

What’s even stranger is once JS is enabled, this behaves as expected but moves further down the <head> element:

Screenshot 2020-05-18 at 16 48 03

This leads me to think next/head is rendering without onLoad on the server, then shuffling around things upon hydration

Copied from #16065 where I have a problem related to what OP is trying to do. If I want to use

<link rel="preload" href="https://..." as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="https://...></noscript>

to make loading CSS async, that does not work because I’d have to rework that to using onLoad with a function, but that function is never executed. So

  1. Is there a way to just insert the above HTML as-is, without having to comply to JSX?
  2. If not, is there a way to make onLoad work?
  3. If not, is there another easy way to make external CSS load async?

Did you find a solution to this? I have the same need.