Webpack 4: mini-css-extract-plugin + file-loader not loading assets - css

I'm trying to move assets (images and fonts) used in one of my .scssfiles, but it seems that they get ignored:
This is my .scss file:
#font-face {
font-family: 'myfont';
src: url('../../assets/fonts/myfont.ttf') format('truetype');
font-weight: 600;
font-style: normal;
}
body {
color: red;
font-family: 'myfont';
background: url('../../assets/images/bg.jpg');
}
And this is my webpack.config.js:
const path = require('path');
const { CheckerPlugin } = require('awesome-typescript-loader');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
target: 'node',
entry: path.resolve(__dirname, 'server.tsx'),
output: {
filename: 'server_bundle.js',
path: path.resolve(__dirname, 'build'),
publicPath: '/build'
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx']
},
module: {
rules: [{
test: /\.(tsx|ts)?$/,
loader: 'awesome-typescript-loader',
options: {
jsx: 'react'
}
},
{
test: /\.(scss|sass|css)$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { url: false, sourceMap: true } },
{ loader: 'sass-loader', options: { sourceMap: true } },
]
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
loader: 'file-loader',
options: { outputPath: 'public/images' }
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
loader: 'file-loader',
options: { outputPath: 'public/fonts' }
}
]
},
plugins: [
new CheckerPlugin(),
new MiniCssExtractPlugin({
filename: 'public/styles_bundle.css',
chunkFilename: "public/styles/[id].css"
})
]
}
I'm getting this .css file in my browser as the output (Note the name of the image):
body {
color: red;
background: url("../../assets/images/myimage.jpg");
}
And in my public directory I get this:
public/
styles_bundle.css
There are two problems here:
Fonts are not compiled (No public/fonts/etc...)
Images are not compiled
I've been trying everything, but I don't know what may be happening here... Any Ideas?

I have just fixed a similar issue. If you change the url option to true, you might see failed image URL references.
{ loader: 'css-loader', options: { url: false, sourceMap: true } },
Or you can manual check whether the path reference is correct.
url('../../assets/images/bg.jpg')
I think the images folder doesn't get created because all the image resource links are incorrect.
For the problem I was fixing, the references were all wrong and I couldn't fix them so I just used this webpack copy plugin to copy files to the right dist folder location.

you need url-loader
{
test: /\.(jpg|png|gif|woff|eot|ttf|svg)/,
use: {
loader: 'url-loader', // this need file-loader
options: {
limit: 50000
}
}
}

try the below configuration
{
test: /\.s[ac]ss$/i,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: ''
}
},
{
loader: "css-loader",
options: { sourceMap: true}
},{
loader: "sass-loader",
options: { sourceMap: true }
}
]
},
{
test: /\.(png|jpg|jpeg|gif)$/i,
loader: 'file-loader',
options: { outputPath: 'assets/images', publicPath: '../images', useRelativePaths: true }
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
loader: 'file-loader',
options: { outputPath: 'assets/fonts', publicPath: '../fonts', useRelativePaths: true }
}
After this my scss file started accepting the relative URL and the compiled files also mapped accordingly using relative path.

The mini-css-extract-plugin has a 'publicPath' option (see here). It basically tells the generated css where the external resources like fonts, images, etc are to be found. In my case, setting it to '../' and configuring all the file loaders to their proper directories, this worked perfectly.
Basically looks like:
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '/public/path/to/', // <-- This is what was helping.
},
},
'css-loader',
],
},
],

I had this issue too. I was using the following versions:
package.json
"mini-css-extract-plugin": "^0.10.1",
"webpack": "^5.3.1",
For me everything was compiling just fine until I added the following into a scss file:
.xmas {
background-image: url("./img/Xmas2020/SurveyBanner.png");
height: 150px;
}
The backgound-image url was the problem. When I changed it to an absolute path it worked:
.xmas {
background-image: url("/img/Xmas2020/SurveyBanner.png");
height: 150px;
}
webpack.config.js
This is just to show how I was putting images into the output directory. I guess there are different ways to do this, but I was using CopyWebpackPlugin.
plugins: [
new CopyWebpackPlugin({
patterns: [
{ from: './src/static/img', to: './img', force: true },
]
}),
]
This link helped me https://github.com/webpack-contrib/mini-css-extract-plugin/issues/286#issuecomment-455917803

Had the exact problem with webpack 5. The answer is in Asset Modules
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
assetModuleFilename: 'images/[hash][ext][query]' //set the pattern for your filename here
},
module: {
rules: [
{
test: /\.png/,
type: 'asset/resource' //replaces file-loader, url-loader etc
}
]
},
};

Related

How to correctly load .otf/.tff fonts in a React app via a Webpack pipeline?

I'm having a hell of a time making this happen and it's possible the various guide I've been following are outdated or I messed something up. I know this has been a common question, and I've played with awesome font, loading it via scss, etc... and it hasn't worked, plus I found it overly complicated where the below approach is also supposed to work and more straight forward.
The webpack error (at the bottom) is thrown by css-loader which isn't the file-loader or url-loader (I tried both), so I suspect the problem is the css-loader is trying to import/find by .otf font file and can't.
If anyone has a clue, it would really help. I need to pack in the font file so my app can run offline.
Here's my file structure:
./webkacp.config.js
./src/
./fonts/AvenirLTStd-Roman.otf
./index.css
./index.jsx
Here's my webpack.config.js:
var HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
mode: 'development',
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
}
}
],
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/'
}
}
],
}
]
},
resolve: {
extensions: ['.js', '.jsx'],
alias: {
'#': path.resolve(__dirname, 'src/'),
}
},
plugins: [new HtmlWebpackPlugin({
template: './src/index.html'
})],
devServer: {
historyApiFallback: true
},
externals: {
// global app config object
config: JSON.stringify({
apiUrl: 'http://127.0.0.1:4000'
})
}
}
Here's my index.css:
#font-face {
font-family: "MyFont";
src: url("./fonts/AvenirLTStd-Roman.otf") format("otf");
/* Add other formats as you see fit */
}
html, body {
font-family: 'MyFont', sans-serif;
}
Here's my index.jsx:
import React from 'react';
import { render } from 'react-dom';
import { App } from './App';
import 'bootstrap/dist/css/bootstrap.min.css';
import '#/index.css';
render(
<App />,
document.getElementById('app')
);
Finally, here's the webpack error:
ERROR in ./src/index.css (./node_modules/css-loader/dist/cjs.js!./src/index.css)
Module not found: Error: Can't resolve './fonts/AvenirLTStd-Roman.otf' in '/var/sphyrna/csdpac-services/images/csdpac-unified/frontend/src'
# ./src/index.css (./node_modules/css-loader/dist/cjs.js!./src/index.css) 4:36-76
# ./src/index.css
# ./src/index.jsx
Got it.
Two changes were needed:
Skip the urls in css-loader so it doesn't try to import/resolve the file.
Remove the san-serif in my css.
Works now. Hope this helps someone else. None of the online guides mention skipping the url.
Here's my updated css:
#font-face {
font-family: "MyFont";
src: url("./fonts/AvenirLTStd-Roman.otf") format("otf");
/* Add other formats as you see fit */
}
html, body {
font-family: 'MyFont';
}
The Webpack.config.js:
var HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
mode: 'development',
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader'
},
{
test: /\.css$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {url:false}
}
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
}
}
],
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/'
}
}
],
}
]
},
resolve: {
extensions: ['.js', '.jsx'],
alias: {
'#': path.resolve(__dirname, 'src/'),
}
},
plugins: [new HtmlWebpackPlugin({
template: './src/index.html'
})],
devServer: {
historyApiFallback: true
},
externals: {
// global app config object
config: JSON.stringify({
apiUrl: 'http://127.0.0.1:4000'
})
}
}

Webpack 4: mini-css-extract-plugin + sass-loader + splitChunks

I've the follow example configuration to use mini-css-extract-plugin with Webpack 4:
entry: {
a: ['./js/a.js', './scss/a.scss'],
b: ['./js/b.js', './scss/b.scss']
},
module: {
rules: [
[...],
{
test: /\.(css|sass|scss)$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 2,
sourceMap: true
}
},
{
loader: 'postcss-loader',
options: {
plugins: () => [
require('autoprefixer')
],
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
},
optimization: {
splitChunks: {
cacheGroups: {
js: {
test: /\.js$/,
name: "commons",
chunks: "all",
minChunks: 7,
},
css: {
test: /\.(css|sass|scss)$/,
name: "commons",
chunks: "all",
minChunks: 2,
}
}
}
},
plugins: [
new MiniCssExtractPlugin({
filename: "dist/[name].css",
}),
]
And the following sass files:
// a.scss
#import 'libA.scss';
#import 'libB.css';
[...]
// b.scss
#import 'libA.scss';
#import 'libB.css';
[...]
When I run webpack libB.css is inserted in in commons.css bundle while libA.scss not.
In general every import of .css file get processed by splitChunks option (only if extension css is specified in the name) while sass import not.
I have a project with multiple sass entry point and many #import of sass component and I'd like to create a common bundle with shared sass modules.
im doing someting like this in my Webpack 4 configuration.
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
module: {
rules: [ {
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
modules: true,
sourceMap: true,
importLoader: 2
}
},
"sass-loader"
]}
]
},
plugins: [
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "style.css",
chunkFilename: "[name].css"
})
]
I also give output.publicPath -configuration object to point into my build dir, example ->
output: {
filename: "[name]-min.js",
path: path.resolve(__dirname, "dist"),
publicPath: "/static/react/dist/"
},
edit If you are interested in code splitting, Webpack 4 can do this "out of the box" for you. This will split .js and as well .css -files for you in case you use dynamic imports.
optimization: {
splitChunks: {
chunks: 'all'
}
}
If you other hand would like to merge only your .css into one file you could add as follow.
optimization: {
splitChunks: {
chunks: 'all'
cacheGroups: {
styles: {
name: 'styles',
test: /\.s?css$/,
chunks: 'all',
minChunks: 1,
reuseExistingChunk: true,
enforce: true,
},
}
},
}

How to config the file path in webpack?

I'm new to webpack. I want to ask how can I custom a file path in webpack.
Here is the code I've tested:
(function () {
var webpack = require('webpack'),
path = require('path'),
BabiliPlugin = require('babili-webpack-plugin'),
ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: {
'site': ['./assets/js/site']
},
output: {
publicPath: '/assets/',
path: path.join(__dirname, 'wwwroot/assets/js'),
filename: '[name].js'
},
module: {
rules: [
{
test: /\.js$/i,
use: [{
loader: 'babel-loader',
query: {
presets: ['es2015', 'stage-1'],
compact: false
}
}]
},
{
test: /(\.(ttf|eot|svg)(\?[\s\S]+)?$)|(\.woff2?(\?v=\d+\.\d+\.\d+)?$)/,
use: [{
loader: 'file-loader',
options: {
name: '../fonts/[name].[ext]'
}
}]
},
{
test: /\.(png|jpe?g|bmp|gif|ico)/,
use: [{
loader: 'file-loader',
options: {
name: '../images/[name].[ext]'
}
}]
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract(['css-loader'])
}
]
},
plugins: [
new BabiliPlugin({}, { test: /\.min\.js$/ }),
new webpack.ProvidePlugin({
$: 'jquery'
}),
new ExtractTextPlugin('../css/bundle.css')
]
};
}());
I want to ask about these lines:
use: [{
loader: 'file-loader',
options: {
name: '../fonts/[name].[ext]'
}
}]
When I set the name starts with ../, it compiles exactly right except the url which is inside the file bundle.css, like this snapshot:
Then, I've tried to remove the characters ../ from the name, the fonts folder was created inside js folder (it's not my goal). But the url inside the file bundle.css folder is right (I want this path - /assets/fonts/...).
How can I edit it?
p/s: May you embed the two images to my question? it doesn't appear now. Thank you!
You may need to set a context in your loader's options. By default the current working directory is used.
I'm not sure what your src directory structure is, so I'm taking a guess.
use: [{
loader: 'file-loader',
options: {
name: 'assets/fonts/[name].[ext]',
context: './src/assets/fonts/'
}
}]

CSS Modules composes not working

I'm trying to setup css modules with postcss + cssnext. It all seems to be working fine, except that the composes keyword is simply not working. The rule vanishes when the bundle is compiled.
Here's my webpack config file:
'use strict'
const path = require('path')
const webpack = require('webpack')
const HtmlPlugin = require('html-webpack-plugin')
module.exports = {
devtool: 'inline-source-map',
entry: [
'react-hot-loader/patch',
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
path.join(__dirname, 'src', 'index')
],
output: {
path: path.join(__dirname, 'dist'),
filename: '[name]-[hash].js',
publicPath: ''
},
plugins: [
// new DashboardPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
new HtmlPlugin({
title: 'Github App',
template: path.join(__dirname, 'src', 'html', 'template-dev.html')
})
],
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
include: /src/,
use: 'babel-loader'
}, {
test: /\.css$/,
exclude: /node_modules/,
include: /src/,
use: ['style-loader', {
loader: 'css-loader',
options: {
modules: true,
importLoaders: 1,
localIdentName: '[local]--[hash:base64:5]'
}
}, {
loader: 'postcss-loader',
options: {
ident: 'postcss'
}
}]
}]
},
resolve: {
alias: {
Src: path.join(__dirname, 'src'),
Components: path.join(__dirname, 'src', 'components')
}
}
}
I'm using style-loader for this dev environment so I can use hot reloading. The css file is being imported like this: import './app.css'
app.css:
:global{
.app {
float: left;
padding: 10px;
width: 100%;
}
}
.className {
color: green;
background: red;
}
.otherClassName{
composes: className;
color: yellow;
}
this results in:
My postcss.config.js file:
module.exports = {
plugins: {
'postcss-import': {},
'postcss-cssnext': {
browsers: ['last 2 versions', '> 5%']
},
'postcss-nested': {}
}
}
Am I missing something to get composes to work?
Looks like this is fine:
The implementation of webpack's css_loader is to add both classes when exporting the styles (see https://github.com/webpack-contrib/css-loader#composing)
This also makes more sense, since it will ultimately render out less CSS code.
Try importing the styles and apply them to an HTML node and you will see it should receive both classes.
In your example it would have done something like:
exports.locals = {
className: 'className-2yqlI',
otherClassName: 'className-2yqlI otherClassName-1qAvb'
}
So when you do:
import styles from '../app.css'
// ...
<div className={styles.otherClassName} />
The div gets both classes.

Load fonts with Webpack and font-face

I'm trying to load a font in my CSS file using #font-face but the font never loads. This is my directory structure.
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.
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' }
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'] }
]
}
};
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.
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/
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.
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.
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!
Try changing your loader to
{test: /\.(eot|svg|ttf|woff|woff2)$/, loader: 'file?name=[name].[ext]'}
and add publicPath: 'build/' to your output key.
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
}
}
]
}
]
}
})

Resources