10

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;
  },

vhs
  • 9,316
  • 3
  • 66
  • 70
Alex
  • 1,210
  • 3
  • 21
  • 34

4 Answers4

8

You don't need to any additional dependency to use fonts. Simply move your fonts to public/fonts instead of static/fonts.

Then to preload:

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

Remember to keep the beginning slash, otherwise it won't work. According to the official docs:

Files inside public can then be referenced by your code starting from the base URL (/).

Also, in JSX, it's crossOrigin not crossorigin, and it expects a string, not a boolean.

And in CSS (add beginning slash to each src-url):

@font-face {
  /* ... */
  src: url('/fonts/Avenir.woff2') format('woff2');
  /* ... */
}

Then remove the custom webpack configuration for font files. A downfall for this method is that the fonts will not be inlined as done by url-loader. But I believe it is quite inefficient to inline all fonts anyway. (You've set a limit of 100000. Almost every woff/woff2 font will be inlined.)

brc-dd
  • 10,788
  • 3
  • 47
  • 67
  • After four hours of trying to get my local font to work on a Nextjs static export site on github pages I found this answer and it solved everything. Thank you – ian-campbell Apr 13 '23 at 22:06
2

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.

vhs
  • 9,316
  • 3
  • 66
  • 70
  • No, one cannot import `WOFF`/`WOFF2` files (in `v10.2.3`) unless they have [manually configured `webpack`](https://nextjs.org/docs/api-reference/next.config.js/custom-webpack-config) to use file loader/asset modules for the font files. – brc-dd May 27 '21 at 06:35
0

In my case, I had my code set up as brc-dd had described, but it still wasn't working: the fonts were downloaded in the network tab, but the text was still unstyled.

In the end, converting my fonts from .ttf to .woff2 fixed the issue for me.

My working code looks like this in Next 11:

// document.js
<Head>
  <link
    rel="preload"
    href="/fonts/Lato/Lato-Light.woff2" // fix: convert .ttf to .woff2
    as="font"
    type="font/woff2"
    crossOrigin="anonymous"
  />
</Head>
// globals.css
@font-face {
  font-family: "Lato";
  font-style: normal;
  font-weight: 300;
  font-display: optional;
  src: url("/fonts/Lato/Lato-Light.woff2") format("woff2");
}

My fonts are stored in project_root/public/fonts/[fontname]/[fontfile].woff2

James Hooper
  • 1,475
  • 13
  • 24
-1

url('/static/fonts/Avenir.eot')

Must include the path like that.

Alex
  • 1,210
  • 3
  • 21
  • 34