18

I'm trying to use some local web fonts in my React project. I'm including them in my main.scss file, and loading them via webpack. The bundle builds correctly, including main.scss styles. I see webpack load the font files, and copy them to public/fonts/, but my bundle file can't find the fonts.

As I understand it, your @font-face src should be in relation to where bundle will be. I'm setting this to the same path that I load the fonts with in webpack, './fonts/'. The exact error I'm seeing is:

file:///Users/myusername/Documents/folder1/folder2/folder3/APP/fonts/FoundersGroteskWeb-Regular.woff net::ERR_FILE_NOT_FOUND

I've been trying a lot of different path configurations, and using the publicPath option in webpack, but I'm going in circles at this point over what seems like a really simple reference error.

File Structure:

APP
├──webpack.config.js
├──src
     ├──app
        ├──App.jsx
        ├──styles
           ├──main.scss
           ├──fonts
              ├──allthefonts.woff
     ├──public
        ├──bundle.js
        ├──fonts
           ├──allthefonts.woff

App.jsx:

require('./styles/main.scss');

main.scss:

 @font-face {
    font-family: FoundersGrotesk;
    src: url('./fonts/FoundersGroteskWeb-Bold.eot') format('eot'),
         url('./fonts/FoundersGroteskWeb-Bold.woff') format('woff'),
         url('./fonts/FoundersGroteskWeb-Bold.woff2') format('woff2');
    font-weight: bold;
}

@font-face {
    font-family: FoundersGrotesk_Cnd;
    src: url('./fonts/FoundersGrotXCondWeb-Bold.eot') format('eot'),
         url('./fonts/FoundersGrotXCondWeb-Bold.woff') format('woff'),
         url('./fonts/FoundersGrotXCondWeb-Bold.woff2') format('woff2');
    font-weight: bold;
}

@font-face {
    font-family: FoundersGrotesk;
    src: url('./fonts/FoundersGroteskWeb-Regular.eot') format('eot'),
         url('./fonts/FoundersGroteskWeb-Regular.woff') format('woff'),
         url('./fonts/FoundersGroteskWeb-Regular.woff2') format('woff2');
    font-weight: normal;
}

webpack.config.js:

 'use strict';

const webpack = require('webpack');
const PROD = JSON.parse(process.env.PROD_ENV || '0');

module.exports = {

  entry: './src/app/App.jsx',

  output: {
    path: './src/public/',
    filename: PROD ? 'bundle.min.js' : 'bundle.js'
  },

  module: {
    loaders: [
      {
        test: /\.jsx?$/,
        loader: 'babel-loader',
        exclude: '/node_modules/',
        query: {
          presets: ['es2015', 'react', 'stage-1']
        }
      },
      {
        test: /\.s?css$/,
        loaders: ['style', 'css', 'sass']
      },
      {
        test: /\.(eot|svg|ttf|woff|woff2)$/,
        loader: 'file-loader?name=./fonts/[name].[ext]'
      }
    ]
  }
};
johnjohn
  • 1,009
  • 1
  • 10
  • 15
  • 5
    Don't bother with `eot` fonts - they're only for IE8 and below, which Microsoft abandoned in January of 2016. IE9 and above all support WOFF. On a different note: don't put your static assets into your bundle, serve them as static content. It saves both you and your users a TON of bandwidth because static assets have always been, and remain to this day, cached by the browser. If you bundle them in, you force massive amounts of data that has not changed over the wire, costing you money, costing your users time, and leading to an unnecessarily poor experience. – Mike 'Pomax' Kamermans Dec 27 '16 at 18:44
  • 2
    @Mike'Pomax'Kamermans I might be misunderstanding, but isn't file-loader just copying the assets from the dev directory to the build directory, so that bundle can easily reference them? That's the approach I've seen in articles/SO posts on working with images or fonts, I don't know how you'd use them otherwise. – johnjohn Dec 28 '16 at 23:45
  • If we're talking about https://github.com/webpack/file-loader, then: sort of, but it does all kinds of funky renaming too. If all you need is asset relocation, using a `cp assets build` instead makes way more sense. You JS code should not need to `require` static assets anyway O_o – Mike 'Pomax' Kamermans Dec 29 '16 at 04:03

3 Answers3

13

Got a working solution thanks to @omerts in this thread. Solution involved using publicPath. I had been trying to use it as an option in module.exports with the fonts file-loader, not the output.

Updated webpack.config.js:

const webpack = require('webpack');
const PROD = JSON.parse(process.env.PROD_ENV || '0');
const path = require('path');

const PATHS = {
  build: path.join(__dirname, './src/public')
};

module.exports = {

  entry: './src/app/App.jsx',

  output: {
    path: PATHS.build,
    filename: PROD ? 'bundle.min.js' : 'bundle.js',
    publicPath: PATHS.build
  },

  module: {
    loaders: [
      {
        test: /\.jsx?$/,
        loader: 'babel-loader',
        exclude: '/node_modules/',
        query: {
          presets: ['es2015', 'react', 'stage-1']
        }
      },
      {
        test: /\.s?css$/,
        loaders: ['style', 'css', 'sass']
      },
      {
        test: /\.(eot|svg|ttf|woff|woff2)$/,
        loader: 'file-loader?name=/fonts/[name].[ext]'
      },
      {
        test: /\.(jpg|png)$/,
        loader: 'file-loader?name=/fonts/[name].[ext]'
      }
    ]
  },

  plugins: PROD ? [
    new webpack.optimize.UglifyJsPlugin({
      beautify: false,
      comments: false,
      compress: { 
        warnings: false,
        drop_console: true
      },
      mangle: {
        except: ['$'],
        screw_ie8: true,
        keep_fnames: false
      }
    })
  ] : []
};
johnjohn
  • 1,009
  • 1
  • 10
  • 15
2

a better approach would be to use 'url-loader' and add the following line in loaders.

{
  test: /\.(jpe?g|png|woff|woff2|eot|ttf|svg)(\?[a-z0-9=.]+)?$/, 
  loader: 'url-loader?limit=100000'
}
Community
  • 1
  • 1
yellowcode
  • 37
  • 3
2

Is it possible to just always reference the original fonts? They don't appear to get changed by file-loader anyways.

File structure

APP
├───build
│   │   build.js
│   │   build.min.js
│   │
│   └───fonts
│           allthefonts.woff
│
├───css
│       main.css
│
├───fonts
│       allthefonts.woff
│
└───js
        main.js

main.css

@font-face {
  font-family: All-The-Fonts;
  src: url('../fonts/allthefonts.woff') format('woff'); 
}

webpack.config.js

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

module.exports = {
  ...
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: "[name].js",
    globalObject: 'this'
  },
  module: {
    rules: [
      {
        test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
        use: [{
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: './fonts/' //dont actually use these fonts but still need to process them
          }
        }]
      }
    ]
  },
  ...
};
Jesse Reza Khorasanee
  • 3,140
  • 4
  • 36
  • 53