29

Fonts using NextJS

I have read different topics about how to use self-hosted fonts with NextJS.

What I got [ wait ] compiling ... when I did:

@font-face {
    font-family: 'montserrat';
    src: url('./mypath/Montserrat-Medium.woff2') format('woff2'),
        url('./mypath/Montserrat-Medium.woff') format('woff'),
        url('./mypath/Montserrat-Medium.ttf') format('truetype'),
        url('./mypath/Montserrat-Medium.svg#Montserrat-Medium') format('svg');
}

No error, or else just compiling... I've read ( stackoverflow/57590195) which says we should use a static path like

@font-face {
    font-family: 'font';
    src: url('./static/fonts/Montserrat-Medium.woff2') format('woff2');
}

but that solution does not work at all. It almost seems to work fine, because the error (or the compelling waiting) stops. But if you look closer your font is not loaded.

Then I tried fontfaceobserver, I understood quickly that the problem would be the same. Because you have to use font-face and you cannot use it with NextJS.

After I downloaded next-font I have read the doc and looked at the github exemples.

Here is my next.config.js inspired of theirs.

const withSass = require('@zeit/next-sass');
const withCSS = require("@zeit/next-css");
const withFonts = require('next-fonts');

module.exports = withFonts(
    withCSS(
        withSass({
            enableSvg: true,
            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;
            }
        })
    )
);

My path to fonts public/static/fonts.

And here is how I tried to use it.

...
function MainIndex() {
    ...
    return (
        <>
        ...
        <style jsx>{`
                @font-face {
                    font-family: 'Montserrat';
                    src: url('./static/fonts/Montserrat-Medium.ttf');
                }
                h1 {
                    font-family: 'Montserrat';
                }
        `}</style>
        </>
    )
}
...

Solutions I found

Read more one the github issue

EDIT:

I tried to adapt what Jesper We did.

I have created /public/static/fonts/fonts.css and /public/fonts/allMyfont.ttf then I imported in _var.scss to use it with sass variable @import "/public/static/fonts/fonts.css"; import style.scss my var $font and import "my/path/style.scss" to my index.js (compling for ever)

After I tried a closer way still /public/static/fonts/fonts.css with my fonts in the same folder. Then in my index.js. But that one does nothing.

Here the code in live CodeSandBox

crg
  • 4,284
  • 2
  • 29
  • 57
  • 2
    Just a quick note here is that you should not use TTF on the Web as they are unoptimized files. If you have a TTF you can use Google's `woff2` cli tool (which bundles with a tool called `woff2_compress`) to convert the TTF file into a WOFF2 compressed file for use on your website. – vhs Aug 31 '20 at 11:39
  • One silly mistake I made when searching this topic: the file extension of my font was capitalized (.TTF not .ttf) ‍♂️ – glimmbo Aug 22 '21 at 19:11

6 Answers6

31

This is what I usually do:

  1. Put the fonts in public/static somewhere

  2. In the same folder as the fonts put a CSS file where the fonts are declared

CSS file example /public/fonts/style.css:

@font-face {
    font-family: 'Italian No1';
    src: url('ItalianNo1-Black.eot');
    src: url('ItalianNo1-Black.eot?#iefix') format('embedded-opentype'), url('ItalianNo1-Black.woff2') format('woff2'), url('ItalianNo1-Black.woff') format('woff');
    font-weight: 900;
    font-style: normal;
}

[UPDATE] For recent versions of NextJS the proper place to import the CSS file is in an import statement in _app.js (See docs).


Older answer:

Import this css in _document.js (docs):

render() {
    return (
        <Html>
            <Head>
                <link href="/fonts/style.css" rel="stylesheet"/>
            </Head>

            <body>
                <Main/>
                <NextScript/>
            </body>
        </Html>
    )
}
Jesper We
  • 5,977
  • 2
  • 26
  • 40
  • As it to be a file named _document.js ? In page/_document.js ? Because my archi is different I tried to adapt what you did but did work here :/ I'll edit my question – crg May 31 '20 at 09:15
  • pages/_document.js is the correct path. I added a link to the relevant docs in the answer. – Jesper We May 31 '20 at 19:34
  • Thank you so much! – iji Feb 02 '21 at 19:25
  • Using a `link` tag in _document.js is not recommended because it can negatively affect CSS resource loading in the app. Next.js's docs mention in it's docs, https://nextjs.org/docs/messages/no-css-tags. If you rung the `eslint` it will warn about this. – ZR87 Oct 02 '21 at 17:23
19

Holy Tip: These days you need not serve multiple font files as variable fonts are widely supported by using just one file. Moreover, the newer font format woff2 has a better compression ratio and is again widely supported.

  1. Put font file in /public/fonts/ folder

  2. Add it to _document.tsx file so that it's fetched for all pages:

export default class MyDocument extends Document {
  render(): JSX.Element {
    return (
      <Html>
        <Head>
          <link
            rel="preload"
            href="/fonts/inter-var-latin.woff2"
            as="font"
            type="font/woff2"
            crossOrigin="anonymous"
          />
        </Head>
        ...
  1. Mention in global CSS file:
@font-face {
    font-family: "Inter";
    font-style: normal;
    font-weight: 100 900;
    font-display: optional;
    src: url(/fonts/inter-var-latin.woff2) format("woff2");
  }
  1. Tell browser to cache this font file for a long time (~1yr) to avoid unnecessary re-downloads for subsequent site visits.
    Add headers to next.config.json:
module.exports = {
  async headers() {
    return [
      {
        source: "/fonts/inter-var-latin.woff2",
        headers: [
          {
            key: "Cache-Control",
            value: "public, max-age=31536000, immutable",
          },
        ],
      },
    ];
  },
};

Reference: https://gourav.io/blog/nextjs-cheatsheet#add-custom-fonts

GorvGoyl
  • 42,508
  • 29
  • 229
  • 225
  • Nice! Btw, just a clarification, it seems like `Cache-Controls` headers are no longer valid within `next.config.js`: https://nextjs.org/docs/api-reference/next.config.js/headers#cache-control – Alejandro Corredor Oct 06 '22 at 23:05
  • Also for clarification, variable fonts are not always preferable. If you for example only need one or two font variants it may be better to include only the specific weight(s). For example: Inter variable is approx. 800kb. If you only use regular and bold you and up with approx. 600kb. 200kb makes a big difference here. – Pawel Oct 24 '22 at 18:27
  • @AlejandroCorredor it seems to be working fine https://cln.sh/OMrxph – GorvGoyl Oct 25 '22 at 11:17
15

I have tried on the latest version of next.js "next": "10.0.8"

Added all of the fonts to public/fonts folder and used in globals.css with font faces.

I think the paths you are using in font face might not be correct but i am not sure about the structure of the code.

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

Afterwards you can use it font-family: 'Open Sans'

Here is a related blog post if you want to read more about

Hadnazzar
  • 1,517
  • 19
  • 21
1

Moving the /fonts/OpenSans.otf directory to public/fonts/OpenSans.otf. Works for me.

Now I can use anywhere with this line:

@font-face {
      font-family: 'OpenSans';
        src: url('/fonts/OpenSans.otf') format('opentype');
        font-weight: normal;
        font-style: normal;
    }
Guri
  • 11
  • 2
0

On v12.0.4, using tailwindcss I had to try few fs changes but landed up with the following that worked. In addition I used both custom fonts and google fonts.

You can also test w/o tailwind but testing inline global CSS:

# _app.js
<div style={{ fontFamily: "Inter"}}>
    <Component {...pageProps} />
</div>

import '../public/styles.css' # didn't work outside public/

# styles.css
# font files are in the same directory
@font-face {
    font-family: 'Avenir';
    src: url('avenir/avenir-light.woff') format('woff');
    font-weight: 100;
    font-style: normal;
}
# tailwind.config.js
    theme: {
        extend: {
            fontFamily: {
                sans: ['Avenir', 'Inter', ...defaultTheme.fontFamily.sans],
                title: ['Avenir', 'Inter', ...defaultTheme.fontFamily.sans]
            }
        }
    },
# _document.js
<Head>
    <link
        href="https://fonts.googleapis.com/css2?family=Inter&display=optional"
        rel="stylesheet"
    />
</Head>
BisonAVC
  • 1,980
  • 2
  • 13
  • 12
0

1-Create a new folder called "fonts" inside the "public" directory of your Next.js project.

2-Add your font files to the "fonts" directory. For example, if you have a font file called "myfont.ttf", you would add it to the "fonts" directory as follows: "public/fonts/myfont.ttf"

3-globals.css

@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont.ttf') format('truetype');
  font-weight: normal;
  font-style: normal;
}

.myFontFamily {
  font-family: 'MyFont', sans-serif;
}

4-index.jsx

<div className="myFontFamily">Hello World!</div>
Sakar
  • 11
  • 3