Search code examples
javascriptreactjsnext.jswebfontsweb-performance

Self-hosted fonts using NextJS


I'm trying to self-host webfonts using my NextJS app and having trouble. This is the URL browser tries to access these fonts:

localhost:3000/_next/static/css/fonts/Avenir.woff2

The actual path is:

_project_dir/static/fonts/Avenir.woff2

I tried including the link in the the _app.js, it does download the fonts, but the text remains unstyled.

<link rel="preload" as="font" href="/static/fonts/Avenir.woff2" type="font/woff2" crossorigin />

Here's my _app.js:


  render() {
    const { Component, pageProps } = this.props;
    return (
      <Container>
        <link href="https://fonts.googleapis.com/css?family=Poppins:500,500i,600&display=swap" rel="stylesheet" />
        <link rel="preload" as="font" href="/static/fonts/Avenir.woff2" type="font/woff2" crossorigin />
        <link rel="preload" as="font" href="/static/fonts/AvenirHeavy.woff2" type="font/woff2" crossorigin />
        <Head>
          <title>Project</title>
        </Head>
        <Provider store={store}>
          <PersistGate loading={null} persistor={persistor}>
            <Component pageContext={this.pageContext} {...pageProps} />
          </PersistGate>
        </Provider>
      </Container>
    );
  }
}

My main.css

@font-face {
  font-family: 'Avenir';
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url('fonts/Avenir.eot');
  src: url('fonts/Avenir.eot?#iefix') format('embedded-opentype'), url('fonts/Avenir.woff2') format('woff2'),
    url('fonts/Avenir.woff') format('woff'), url('fonts/Avenir.ttf') format('truetype');
}

@font-face {
  font-family: 'Avenir';
  font-weight: 500;
  src: url('fonts/Avenir.eot');
  src: url('fonts/Avenir.eot?#iefix') format('embedded-opentype'), url('fonts/Avenir.woff2') format('woff2'),
    url('fonts/Avenir.woff') format('woff'), url('fonts/Avenir.ttf') format('truetype');
}

@font-face {
  font-family: 'Avenir';
  font-weight: 900;
  src: url('fonts/AvenirHeavy.eot');
  src: url('fonts/AvenirHeavy.eot?#iefix') format('embedded-opentype'), url('fonts/AvenirHeavy.woff2') format('woff2'),
    url('fonts/AvenirHeavy.woff') format('woff'), url('fonts/AvenirHeavy.ttf') format('truetype');
}

And my next.config.js:

  webpack(config, options) {
    config.module.rules.push({
      test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
      use: {
        loader: 'url-loader',
        options: {
          limit: 100000,
        },
      },
    });
    return config;
  },


Solution

  • In newer versions of next I believe you can import the WOFF2 file and use that in your CSS similar to this example for Gatsby. However, if you're not importing font files and instead placing them in the /static/fonts directory as you explain you can avoid using the WebPack loader or a plugin like next-fonts by hard-coding the font paths in your static directory as suggested by Alex:

    import React, { Fragment } from "react";
    
    const WebFonts = () => (
      <Fragment>
        <style global jsx>{`
          @font-face {
            font-family: "Source Sans Pro";
            font-style: normal;
            font-stretch: normal;
            font-weight: 400;
            font-display: fallback;
            src: local("SourceSansPro Regular"), local("SourceSansPro-Regular"),
              url(/static/fonts/SourceSansPro-Regular.woff2) format("woff2");
            unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f,
              U+A720-A7FF;
          }
          @font-face {
            font-family: "Source Sans Pro";
            font-style: normal;
            font-weight: 600;
            font-display: fallback;
            src: local("SourceSansPro SemiBold"), local("SourceSansPro-SemiBold"),
              url(/static/fonts/SourceSansPro-SemiBold.woff2) format("woff2");
            unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f,
              U+A720-A7FF;
          }
          @font-face {
            font-family: "Source Sans Pro";
            font-style: normal;
            font-weight: 700;
            font-display: fallback;
            src: local("SourceSansPro SemiBold"), local("SourceSansPro-SemiBold"),
              url(/static/fonts/SourceSansPro-Bold.woff2) format("woff2");
            unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f,
              U+A720-A7FF;
          }
        `}</style>
      </Fragment>
    );
    
    export default WebFonts;
    

    And then importing that Component into your _document override in NextJS. This will use fonts added to the NextJS static directory. Be sure to compress any TTF font downloads from Google Fonts using the woff2_compress method provided by woff2 before serving them for better page speed. And if you're not seeing your local font downloads appearing in the Network waterfall in DevTools make sure you test by removing any local versions of those fonts or local font face declarations.