I'm fairly new to Webpack 5, while my build is almost working, my fonts are not being transfered to my /dist folder. I include the fonts in my main.scss file (see below)
Am I missing something with my current config?
folder structure
- src
- assets
- sass
- fonts
src/assets/sass/main.scss
#font-face {
font-family: 'P22MackinacMedium';
src: url('../fonts/p22-mackinac-medium.eot');
src: url('../fonts/p22-mackinac-medium.eot?#iefix') format('embedded-opentype'),
url('../fonts/p22-mackinac-medium.woff2') format('woff2'),
url('../fonts/p22-mackinac-medium.woff') format('woff'),
url('../fonts/p22-mackinac-medium.ttf') format('truetype'),
url('../fonts/p22-mackinac-medium.svg#youworkforthem') format('svg');
font-weight: normal;
font-style: normal;
}
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: 'development',
entry: './src/index.js',
plugins: [new MiniCssExtractPlugin(), new HtmlWebpackPlugin({
template: 'src/index.html'
})],
output: {
filename: '[name]-[contenthash].js',
chunkFilename: '[name]-[chunkhash].js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
}
},
module: {
rules: [
{
test: /\.css$/,
use: [
// Creates `style` nodes from JS strings
"style-loader",
// Translates CSS into CommonJS
"css-loader",
],
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, {
loader: 'css-loader',
options: { url: false }
},
'sass-loader'
],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
],
},
devServer: {
static: './dist',
},
};
As far as I can see this should be working out of the box, like you wrote it, since webpack 5 is using new Assets management. But, since it is not working for you, you can still try to use url-loader. Like this:
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
use: {
loader: 'url-loader',
},
},
Or you could try 'asset/inline' before installing url loader.
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'
})
}
}
in my React app i'm implementing server side rendering so i don't want to add node_modules in my server side because of performance issue.But i want css from from node_modules which are used as UI Kit libs.so i used webpack nodeExternal in my webpack.server.config file like
const path = require('path');
const merge = require('webpack-merge');
const baseConfig = require('./webpack.base.js');
const nodeExternals = require('webpack-node-externals')
let test = {
test: /\.css$/,
loader: 'css/locals?module&localIdentName=[name]__[local]___[hash:base64:5]'
}
const config = {
target:'node',
entry: './src/index.js',
output:{
filename:'bundle.js',
path:path.resolve(__dirname,'build')
},
externals: [nodeExternals({
whitelist: [/\.(?!(?:jsx?|json)$).{1,5}$/i]
})]
}
module.exports = merge(baseConfig,config)
here webpack adding css files but it is not loading #font-face from my node_modules UI kit files.if i remove nodeExternal from webpack.server.config file then every thing is working fine but bundle.js file size getting increased.
i already added font rule in my webpack.base.config file
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CSSExtract = new ExtractTextPlugin('styles.css');
module.exports ={
module: {
rules: [{
loader: 'babel-loader',
test: /\.js$/,
exclude: /node_modules/
}, {
test: /\.s?css$/,
use: CSSExtract.extract({
use: [
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
})
},{
test: /\.(gif|svg|jpg|png|ttf|eot|woff(2)?)(\?[a-z0-9=&.]+)?$/,
loader: 'url-loader?limit=100000&name=fonts/[name].[ext]'
}
],
},
devtool: 'inline-source-map',
plugins: [
CSSExtract
]
}
i tried with file-loader also but same problem
it showing me error
#font-face {
^
SyntaxError: Invalid or unexpected token
How can i add css files from node_modules with #font-face
here is webpack set up:
{
test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: "file-loader",
options: {
name: "[name].[contenthash].[ext]",
outputPath: "fonts/",
},
},
],
},
then you have to import them to the css or scss main file.
#font-face {
font-family: "Montserrat";
src: url("../../assets/fonts/Montserrat-Light.ttf");
font-weight: lighter;
font-style: normal;
}
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
}
}
]
}
]
}
})
Using FontFaceObserver and getting a webpack error.
Failed to decode downloaded font
OTS parsing error: invalid version tag
Webpack config:
var path = require('path')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'#': resolve('src'),
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
query: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
query: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
},
{
test: /\.mp4$/,
loader: 'url-loader',
query: {
limit: 10000,
name: utils.assetsPath('videos/[name].[hash:7].[ext]')
}
}
]
}
}
At the top of App.vue stylesheet
#font-face {
font-family: Pragmatica;
font-weight: bold;
src: url('/static/fonts/PragmaticaCond-Extrabold.woff') format('woff');
}
#font-face {
font-family: Pragmatica;
src: url('/static/fonts/Pragmatica-Book.woff') format('woff');
}
My fonts sit within static/fonts/.
Any ideas?
It was a combination of a couple things. first was the path to the font file, I was setting the path as if there was no “build” happening, Webpack was putting them under /assetsafter release build so I had to update my path from /static/fonts to /fonts as after the build static files are automagically looked under assets folder + adding regex to support versioning in my webpack loader config + adding mimetype for woff files