17

Currently I have 3 fonts that I want to add to my React project:a, a light, a bold.
My file structure:

/src
├── /fonts/
│   ├── /A.ttf
│   ├── /A-light.ttf
│   └── /A-bold.ttf
│  
├── /styles/
│   ├── /base/
│   │   └── /__base.scss
│   └── styles.scss
│ 
├── app.jsx
└── webpack.config.js

_base.scss:

@font-face {
  font-family: "A";
  font-style: normal;
  font-weight: 400;
  src: url("../../fonts/A.ttf") format("truetype");
}

@font-face {
  font-family: "A";
  font-style: normal;
  font-weight: 800;
  src: url("../../fonts/A-bold.ttf") format("truetype");
}
@font-face {
  font-family: "A";
  font-style: normal;
  font-weight: 300;
  src: url("../../fonts/A-light.ttf") format("truetype");
}
body {
  font-family: A, Helvetica, Arial, sans-serif;
}

_base.scss is imported by styles.scss and styles.scss is imported in app.jsx.

My webpack config looks like this:
webpack.config.js

const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const StyleLintPlugin = require('stylelint-webpack-plugin');

process.env.NODE_ENV = process.env.NODE_ENV || 'development';
console.log(process.env.NODE_ENV);
if (process.env.NODE_ENV === 'development') {
  require('dotenv').config({path: '.env.development'});
}

module.exports = env => {
  const isProduction = env === 'production';
  const CSSExtract = new ExtractTextPlugin('styles.css');

  return {
    entry: ['babel-polyfill', './src/app.jsx'],
    output: {
      path: path.join(__dirname, 'public', 'dist'),
      filename: 'bundle.js'
    },
    resolve: {
      extensions: ['.js', '.jsx', '.json', '.css', '.scss']
    },
    module: {
      rules: [
        {
          exclude: /(node_modules|bower_components)/,
          test: /\.jsx?$/,
          use: ['babel-loader', 'eslint-loader']
        },
        {
          test: /\.s?css$/,
          use: CSSExtract.extract({
            use: [
              {
                loader: 'css-loader',
                options: {
                  sourceMap: true
                }
              },
              {
                loader: 'sass-loader',
                options: {
                  sourceMap: true
                }
              }
            ]
          })
        },
        {
          test: /\.(png|jpg|svg)$/,
          use: {
            loader: 'url-loader'
          }
        },
        {
          test: /\.(ttf|eot|woff|woff2)$/,
          loader: 'file-loader',
          options: {
            name: 'fonts/[name].[ext]'
          }
        }
      ]
    },
    plugins: [
      CSSExtract,
      new webpack.DefinePlugin({
        'process.env.API_AUTH_TOKEN': JSON.stringify(process.env.API_AUTH_TOKEN),
        'process.env.API_EMAIL': JSON.stringify(process.env.API_EMAIL),
        'process.env.API_PASSWORD': JSON.stringify(process.env.API_PASSWORD)
      }),
      new StyleLintPlugin({})
    ],
    devtool: isProduction ? 'source-map' : 'inline-source-map',
    devServer: {
      overlay: {
        warnings: true,
        errors: true
      },
      contentBase: path.join(__dirname, 'public'),
      historyApiFallback: true,
      publicPath: '/dist/'
    }
  };
};

However Webpack fails to compile.

Error:

./src/styles/styles.scss Module build failed: ModuleNotFoundError: Module not found: Error: Can't resolve '../../fonts/A.ttf'

Any help will be appreciated!

Boussadjra Brahim
  • 82,684
  • 19
  • 144
  • 164
greenN
  • 171
  • 1
  • 1
  • 4
  • I realize this was a long time ago, but shouldn't your src statement `src: url("../../fonts/A.ttf")` have an extra `../` to find the fonts directory? – mcheah Jul 11 '18 at 17:00
  • 4
    Not a single resource on the entire internet that shows a full, simple working example. Incredible. – html_programmer Mar 28 '20 at 04:31

5 Answers5

15

Using 'ttf-loader' from npm worked perfectly for me.

https://www.npmjs.com/package/ttf-loader

module: {
  rules: [
    {
      test: /\.ttf$/,
      use: [
        {
          loader: 'ttf-loader',
          options: {
            name: './font/[hash].[ext]',
          },
        },
      ]
    }
  ]
}
suntzu
  • 298
  • 2
  • 10
14

As of webpack 5 you can use built-in asset modules instead. From the official Webpack guide here, add this to module.rules to load .ttf files:

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

Lakshay Akula
  • 141
  • 1
  • 3
8

Re Webpack 5: In case this is helpful to anyone else, here is a solution to what seems to be a common issue experienced when loading truetype fonts using Webpack 5. It's actually a CSS issue.

In the Webpack 5 Asset Management section of the docs, under the heading "Loading Fonts", the style.css file has the following example @font-face declaration:

@font-face {
   font-family: 'MyFont';
    src: url('./my-font.woff2') format('woff2'),
    url('./my-font.woff') format('woff');
}

In order for this declaration to work with other font types, such as truetype or opentype fonts, the format needs to be specified correctly. Any of the following format types are valid:

"woff", "woff2", "truetype", "opentype", "embedded-opentype", "svg".

If you set the format type to "ttf" or "otf", for example, the font will not be imported. The format type is there to tell the browser the font's format but is not compulsory, so, if you leave it out, your font should still load correctly.

Assuming you have set up the path to your font correctly, this example should work as expected:

@font-face {
   font-family: 'MyFont';
   src: url('./my-font.ttf') format('truetype');
}

This should also work:

@font-face {
   font-family: 'MyFont';
   src: url('./my-font.ttf');
}
Sideshowlol
  • 131
  • 1
  • 5
2

Webpack requires a font loader to load font files present in your project. you are using a file loader to load fonts. Change

{
      test: /\.(ttf|eot|woff|woff2)$/,
      loader: 'file-loader',
      options: {
      name: 'fonts/[name].[ext]'
 }

to

 {
      test: /\.ttf$/,
      use: [
        {
          loader: 'ttf-loader',
          options: {
            name: './font/[hash].[ext]',
          },
        },
      ]
  }

by installing a font loader in your project like TTF Loader from NPM.

Tridev Mishra
  • 371
  • 2
  • 12
  • 1
    Thanks for the response, but unfortunately that didn't help. Also I've changed "'./font/[hash].[ext]'" to "name: './fonts/[hash].[ext]'". – greenN Apr 17 '18 at 13:06
  • You should import ttfs in your code! import someFontFamily from 'name-of-font.ttf'; – CrowScript Feb 14 '20 at 13:26
0

Prefix the font path with ~, to tell Webpack that this is not a relative import https://github.com/webpack-contrib/sass-loader#imports

an nguyen
  • 1
  • 1