11
votes

I'm using global style from styled components with next.js and every time I reload my page I can see the font flickering.

font flash

I have my font files in public/fonts/Inconsolata

I've looked everywhere in spectrum chat, next.js github issues but can't seem to find any solution.

pages/index.js

import styled from 'styled-components';

const H1 = styled.h1`
  font-family: 'Inconsolata Bold';
  font-weight: 700;
  color: #000;
`;

const index = () => {
  return (
    <div>
      <H1>font flashes</H1>
    </div>
  );
};

export default index;

pages/_app.js

import App from 'next/app';
import React from 'react';

import GlobalStyle from '../src/style/globalStyle';

export default class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props;
    return (
      <>
        <GlobalStyle />
        <Component {...pageProps} />
      </>
    );
  }
}

pages/_document.js

import Document from 'next/document';
import { ServerStyleSheet } from 'styled-components';

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

    try {
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: App => props => sheet.collectStyles(<App {...props} />)
        });

      const initialProps = await Document.getInitialProps(ctx);
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        )
      };
    } finally {
      sheet.seal();
    }
  }
}

style/globalStyle.js

import { createGlobalStyle } from 'styled-components';

const globalStyle = createGlobalStyle`
  @font-face {
    font-family: 'Inconsolata';
    src:  url('./fonts/Inconsolata/Inconsolata-Regular.woff2') format('woff2'),
          url('./fonts/Inconsolata/Inconsolata-Regular.woff') format('woff');
    font-weight: 400;
    font-style: normal;
    font-display: fallback;
  }
  @font-face {
    font-family: 'Inconsolata Bold';
    src:  url('./fonts/Inconsolata/Inconsolata-Bold.woff2') format('woff2'),
          url('./fonts/Inconsolata/Inconsolata-Bold.woff') format('woff');
    font-weight: 700;
    font-style: bold;
    font-display: fallback;
  }
`;

export default globalStyle;
2
I have the same problemJose Henrique
You could resolved it?Andrés Montoya

2 Answers

5
votes

UPDATE:

Next.js released a new feature called Automatic Webfont Optimization.

Just include your font (it works only with Google Fonts so far) and it will be included as raw css on build-time.

// Before
<link
  href="https://fonts.googleapis.com/css2?family=Inter"
  rel="stylesheet"
/>

// After
<style data-href="https://fonts.googleapis.com/css2?family=Inter">
  @font-face{font-family:'Inter';font-style:normal.....
</style>

Check out how Next.js guys handle it on their website, which is open-source and can be found here. Check it out, it worked for me.

  1. Import your used font in css @font-face via preload link

    <link
        rel="preload"
        href="/assets/my-font.woff2"
        as="font"
        type="font/woff2"
    />
    
  2. Your font declaration should be on SSR HTML page, so use <style jsx global /> to include it in your page. It can be an external file or right directly in style element.

-1
votes

I had the same problem and after hours of experimenting with different methods npm package fontfaceobserver solved the problem for me.

With the package you can tell your app to render only after fonts are loaded, thus avoiding FOUT like so:

import FontFaceObserver from "fontfaceobserver";

const font = new FontFaceObserver("Inconsolata");

font.load().then(()=> {
  ReactDOM.render(<App />,document.getElementById("root"));
}