60

I'm trying to load a font in my CSS file using @font-face but the font never loads. This is my directory structure.

enter image description here

Then in webpack.config.js I have the loader to get fonts.

var path = require('path');
var webpack = require('webpack');

module.exports = {
  devtool: 'eval',
  entry: [
    "./index.js"
  ],
  output: {
    path: __dirname+"/build",
    filename: "main.js"
  },
  plugins: [
    new webpack.NoErrorsPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ],
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  module: {
      loaders: [
          { test: /\.js$/, loaders: ['react-hot', 'babel-loader'], exclude: /node_modules/ },
          { test: /\.jsx?$/, loaders: ['react-hot', 'babel-loader'], exclude: /node_modules/ },
          { test: /\.svg$/, loader: "raw" },
          { test: /\.css$/, loader: "style-loader!css-loader" },
          { test: /\.(eot|svg|ttf|woff|woff2)$/, loader: 'file?name=src/css/[name].[ext]'}

      ]
  }
};

Inside my CSS file I have this:

@font-face {
  font-family: 'Darkenstone';
  src: url('./Darkenstone.woff') format('woff');
}

body {
  background-color: green;
  font-size: 24px;
  font-family: 'Darkenstone';
}

Finally, I'm calling my CSS file in my index.js with:

import './src/css/master.css';

Everything works but de font never loads.

Ebenizer Pinedo
  • 1,261
  • 1
  • 10
  • 13

9 Answers9

47

After trying a lot of stuff the next loader made the work. Instead of file-loader, I used url-loader . You need url-loader installed.

{ test: /\.(png|woff|woff2|eot|ttf|svg)$/, loader: 'url-loader?limit=100000' }
Ebenizer Pinedo
  • 1,261
  • 1
  • 10
  • 13
  • 23
    Inline Base64 is not a good idea, as outer resources can be cached more efficiently. And as an addition it interprets 30% slower on Mobile devices which is a huge difference. – Eru Jun 25 '18 at 21:56
  • 1
    you saved my life after a couple of hours struggling with this error! – Narges Moemenyan Oct 25 '20 at 15:45
  • 5
    This is no longer the answer if you're using Webpack 5 and up. See @arbob's answer that includes "asset/resource" in it. – trusktr Oct 27 '21 at 13:49
21

According to Webpack's Documentation, you need to update your webpack.config.js to handle font files. See the last loader i.e.

{
   test: /\.(woff|woff2|eot|ttf|otf)$/i,
   type: 'asset/resource',
  }

We will include all kinds of file format for webpack. The final webpack.config.js file will look something like this.

const path = require('path');
module.exports = {
       entry: './src/index.js',
       output: {
         filename: 'bundle.js',
         path: path.resolve(__dirname, 'dist'),
       },
       module: {
         rules: [
           {
             test: /\.css$/i,
             use: ['style-loader', 'css-loader'],
           },
           {
             test: /\.(png|svg|jpg|jpeg|gif)$/i,
             type: 'asset/resource',
           },
          {
            test: /\.(woff|woff2|eot|ttf|otf)$/i,
            type: 'asset/resource',
          },
         ],
       },
     };

Then, you will have to import the fonts to your entry file. In this case ./src/index.js. With the loader configured and fonts in place, you can incorporate them via an @font-face declaration. The local url(...) directive will be picked up by webpack just as it was with the image.

arbob
  • 239
  • 2
  • 6
  • 3
    This is the correct answer as of Webpack 5. Anything else involving a loader is outdated. file-loader is deprecated: https://v4.webpack.js.org/loaders/file-loader/ – trusktr Oct 27 '21 at 13:48
19

With webpack 4 this is what solved the issue for me (diff):

       {
         test: /\.svg$/,
         use: ['svg-loader']
       },
       {
         test: /\.(eot|woff|woff2|svg|ttf)([\?]?.*)$/,
         use: ['file-loader']
       }
       { test: /\.(png|woff|woff2|eot|ttf|svg)$/, use: ['url-loader?limit=100000'] }

I had to remove svg-loader and file-loader in favor of url-loader

My app.scss file looks like this:

$fa-font-path: '~font-awesome/fonts';
@import '~font-awesome/scss/font-awesome';

$icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/";
@import '~bootstrap-sass/assets/stylesheets/bootstrap';

And in my app.js I import the app.scss:

import './app.scss';

So, after the changes, my webpack config looks like this:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpackConfig = require('./webpack.config');

module.exports = {
  entry: {
    app: './client/app/app.js'
  },
  devtool: 'source-map',
  mode: 'development',
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Development',
      template: 'client/index.html'
    })
  ],
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader',
        ],
      },
      { test: /\.(png|woff|woff2|eot|ttf|svg)$/, use: ['url-loader?limit=100000'] }
    ]
  }
};
Sauce
  • 652
  • 6
  • 12
Cat Bakun
  • 199
  • 1
  • 6
  • Update: Compiling RuleSet failed: Query arguments on 'loader' has been removed in favor of the 'options' property. So that just needs a change – Eggcellentos Jun 02 '22 at 12:31
7

I got some issues with my font faces when I updated from Webpack 4.x.x to latest 5.52.1

The content of my exported .woff / .woff2 / .ttf ... looked something like this:

export default __webpack_public_path__ + "glyphicons-halflings-regular.woff2";

The solution for me was to simply remove my previous file-loader entirely. It seems the files were being processed multiple times, which caused the error.

Before:

...
module: {
    rules: [
        {
            test: /\.(png|jpg|gif|ico|woff|woff2|ttf|svg|eot)$/,
            use: {
                loader: 'file-loader',
                options: { 
                    name: '[name].[ext]',
                    useRelativePath: true
                }
            }
        },
        {
            test: /\.css$/,
            use: [
                MiniCssExtractPlugin.loader,
                {
                    loader: 'css-loader',
                    options: { url: false }
                }
            ]
        }
    ]
}
...

After:

...
module: {
    rules: [
        {
            test: /\.css$/,
            use: [
                MiniCssExtractPlugin.loader,
                {
                    loader: 'css-loader',
                    options: { url: false }
                }
            ]
        }
    ]
}
...

For more information: https://webpack.js.org/guides/asset-modules/

Arg0n
  • 8,283
  • 2
  • 21
  • 38
4

I was having the same problem. In my case, the 'src' of the font became src="[object Module]".

Disabling esModule on webpack was the solution:

{
  test: /\.(png|jpe?g|gif|svg|ttf|woff|otf)$/,
  use: [
    {
      loader: 'file-loader',
      options: {
        name: '[name].[contenthash].[ext]',
        outputPath: 'static/img',
        esModule: false // <- here
      }
    }
  ]
}

More info on it here.

Antonio
  • 2,848
  • 2
  • 15
  • 15
  • that did the trick for me, but using url-loader, not file-loader. I'm using webpack 4.43.0 – Dex Aug 08 '20 at 19:16
4

This took me a little while to figure out so I'm posting here in case this helps anyone in the future. If you're using regular css on a node js / webpack build this solution worked for me:

module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: "babel-loader",
        options: {
          presets: ["@babel/preset-env"],
        },
      },
    },
    {
      test: /\.html$/,
      exclude: /node_modules/,
      use: "html-loader",
    },
    {
      test: /\.(png|svg|jpg|jpeg|gif)$/i,
      type: "asset/resource",
      generator: {
        filename: "assets/[name][ext][query]",
      },
    },
    {
      test: /\.(woff|woff2|eot|ttf|otf)$/i,
      type: "asset/resource",
      generator: {
        filename: "fonts/[name][ext][query]",
      },
    },
  ],
},

Add the import statement needed in your .js file:

// images references in the manifest
import "../../assets/icon-16.png";
import "../../assets/icon-32.png";
import "../../assets/icon-64.png";
import "../../assets/icon-80.png";
import "../../assets/icon-25.png";
import "../../assets/icon-48.png";
//fonts referenced in the css
import "../../Fonts/opensans-regular-webfont.eot";
import "../../Fonts/opensans-regular-webfont.svg";
import "../../Fonts/opensans-regular-webfont.ttf";
import "../../Fonts/opensans-regular-webfont.woff";
import "../../Fonts/opensans-regular-webfont.woff2";
import "../../Fonts/OpenSans-Regular.ttf";

Then all you need to do is update your .css file to point to the dist/fonts file created:

  @font-face {
  font-family: 'open_sansregular';
  src: url('fonts/opensans-regular-webfont.eot');
  src: url('fonts/Fonts/opensans-regular-webfont.eot?#iefix') format('embedded-opentype'), url('fonts/opensans-regular-webfont.woff2') format('woff2'), url('fonts/opensans-regular-webfont.woff') format('woff'), url('fonts/opensans-regular-webfont.ttf') format('truetype'), url('fonts/opensans-regular-webfont.svg#open_sansregular') format('svg');
  font-weight: normal;
  font-style: normal;
}

html,
body {
    font-family: open_sansregular !important
}

And then build the project.

skerr4311
  • 113
  • 6
2

In the webpack.config.ts I needed two things: Webpack will handle font files with this code

module: {
  rules: [
    {
      test: /\.(woff|woff2|eot|ttf|otf)$/i,
      type: 'asset/resource',
    }
  ]
}

AND then I needed css-loader for webpack to understand @font-face and and url() and style-loader to inject the css into the DOM.

module: {
  rules: [
    {
      test: /\.(woff|woff2|eot|ttf|otf)$/i,
      type: 'asset/resource',
    },
    {
      test: /\.css$/i,
      use: [
        'style-loader',
        'css-loader'
      ]
    },
  ]
}

Then in your index.css file under src add (My font was called Exo):

@font-face {
  font-family: "Exo";
  src: local(Exo), // This names it to use in css
    url('./fonts/exo-v20-latin/exo-v20-latin-regular.woff2') format('woff2'),
    url('./fonts/exo-v20-latin/exo-v20-latin-regular.woff') format('woff');
  font-weight: normal;
  font-style: normal;
}

 html, body {
  font-family: Arial, Helvetica, sans-serif;
 }

I recommend woff formats because they handle all modern browsers.

Make sure to add your css file to your main app index.js:

import './index.css';

Then you might want to build so those files end up in your build/dist/public folder (whatever you've named your build folder).

npm run build

OR

yarn run build

This worked for me. Hope it works for you!

Carter
  • 21
  • 1
1

Try changing your loader to

{test: /\.(eot|svg|ttf|woff|woff2)$/, loader: 'file?name=[name].[ext]'}

and add publicPath: 'build/' to your output key.

bcb
  • 1,977
  • 2
  • 22
  • 21
Esben
  • 1,943
  • 1
  • 18
  • 37
  • In my output path (webpack.config.js) I have "build", so with that loader I get another build folder inside build. Something like build/build. – Ebenizer Pinedo Aug 03 '17 at 18:28
  • Oh okay, I have removed build/ from the loader. That should put the font in the root of your build folder, yeah? Then add `publicPath: 'build/'` to `output`. You can open index.html and find the style tag and see what the url of the font is, it should be `build/Darkenstone.woff` – Esben Aug 03 '17 at 18:53
  • I get this error in mi console: 95% emitError: EACCES: permission denied, open '/Darkenstone.woff' at Error (native) I've removed Path: __dirname+"/build" to add publicPath, is this ok? – Ebenizer Pinedo Aug 03 '17 at 19:22
  • No, you should have both `path: __dirname + '/build'` and `publicPath: 'build/'`. Please add the contents of `index.html` to your question. – Esben Aug 04 '17 at 04:22
  • 1
    Actually I've resolved my problem with a different loader, it's in the answer below, but thank you so much for your help ;) – Ebenizer Pinedo Aug 04 '17 at 05:42
  • ```BREAKING CHANGE: It's no longer allowed to omit the '-loader' suffix``` So, it should be: ``` {test: /\.(eot|svg|ttf|woff|woff2)$/, loader: 'file-loader?name=[name].[ext]'} ``` – mykeels Jul 14 '20 at 11:10
0

I bumped webpack version to 4.42.0 from 4.20.2 and my code started loading the font I want.

const URL_LOADER_LIMIT = 8192

module.exports = () => ({
  module: {
    rules: [
      {
        test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              name: '[name].[ext]',
              outputPath: 'fonts/',
              limit: URL_LOADER_LIMIT
            }
          }
        ]
      },
      {
        test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              name: '[name].[ext]',
              outputPath: 'fonts/',
              limit: URL_LOADER_LIMIT,
              mimetype: 'application/font-woff'
            }
          }
        ]
      },
      {
        test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              name: '[name].[ext]',
              outputPath: 'fonts/',
              limit: URL_LOADER_LIMIT,
              mimetype: 'application/octet-stream'
            }
          }
        ]
      },
      {
        test: /\.mp3$/,
        use: ['file-loader']
      },
      {
        test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              outputPath: 'images/',
              name: '[name].[ext]',
              limit: URL_LOADER_LIMIT,
              mimetype: 'image/svg+xml'
            }
          }
        ]
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              outputPath: 'images/',
              name: '[name].[ext]',
              limit: URL_LOADER_LIMIT
            }
          }
        ]
      }
    ]
  }
})

Fethi Wap
  • 31
  • 1
  • 5
  • 1
    This answer doesn't provide a solution that actually tackles the issue. Just suggesting to update a version is all right, but possibly not the issue here, as you also add a long config, please give an explanation as well and what is possibly different in this config. – Julian Kleine Jan 22 '21 at 23:54