This question already has answers here:
Self-hosted fonts using NextJS
(4 answers)
Closed 1 year ago.
I have a problem when I try to add self hosted font in Material UI 5 with Next.js. I got this error:
You may need an appropriate loader to handle this file type, currently
no loaders are configured to process this file. See
https://webpack.js.org/concepts#loaders (Source code omitted for this
binary file)
Even I have added file-loader in next.config.js here:
module.exports = {
module: {
rules: [
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader'],
},
],
},
};
And this is my custom theme:
PS: my font name in local is BarcadeBrawl.ttf.
import { createTheme } from "#mui/material";
import { purple, blue } from '#mui/material/colors';
import BarcadeBrawl from '../assets/fonts/BarcadeBrawl.ttf'
export const theme = createTheme({
palette: {
primary: {
main: purple[500],
},
secondary: {
main: blue[500],
},
},
typography: {
fontFamily: 'BarcadeBrawl',
fontSize: 12,
},
components: {
MuiCssBaseline: {
styleOverrides: `
#font-face {
font-family: 'BarcadeBrawl';
font-style: normal;
font-display: swap;
font-weight: 400;
src: local('BarcadeBrawl'), local('BarcadeBrawl'), url(${BarcadeBrawl}) format('ttf');
unicodeRange: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF;
}
`,
},
},
});
Firstly, that's not how Webpack is configured in Next.js. Refer the official documentation on this - Custom Webpack Config. Secondly, file-loader is deprecated for Webpack v5 (the default for newer Next.js versions). Use asset modules instead.
So you probably need to do something like this:
// next.config.js
// https://webpack.js.org/guides/asset-management/#loading-fonts
module.exports = {
// other configs...
// future: { webpack5: true }, // -- not needed since Next.js v11.0.0
webpack(config) {
config.module.rules.push({
test: /\.(woff|woff2|eot|ttf|otf)$/i,
issuer: { and: [/\.(js|ts|md)x?$/] },
type: 'asset/resource',
});
return config;
},
};
Moreover, this is not necessary, you can simply store your fonts directory in public directory and use them. There is no need to import them. They can be directly referenced like /fonts/BarcadeBrawl.ttf. Refer: Static File Serving
Related
I added font to react typescript project after that I try to build but its showing error
Error during bundle: Error: Unexpected character '' (Note that you need plugins to import files that are not JavaScript)
Adding font using styled-components and loader using is url-loader
fonts work fine in system and no error while run.
Font extension is ttf. Below is my style file using styled-component
import IBMPlexSansSemiBold from './fonts/IBMPlexSans/IBMPlexSans-SemiBold.ttf';
import IBMPlexSansBold from './fonts/IBMPlexSans/IBMPlexSans-Bold.ttf';
import { font} from './Fonts';
export const FontsIBM = createGlobalStyle`
#font-face {
font-family: ${font.DEFAULT.SEMI_BOLD};
src: local('IBMPlexSans-SemiBold'), local(${font.DEFAULT.SEMI_BOLD}),
url(${IBMPlexSansSemiBold}) format('truetype');
font-weight: 600;
font-style: normal;
}
#font-face {
font-family: ${font.DEFAULT.BOLD};
src: local('IBMPlexSans-Bold'), local(${font.DEFAULT.BOLD}),
url(${IBMPlexSansBold}) format('truetype');
font-weight: 700;
font-style: normal;
}
`;
This is how I config url-loader
const rootMain = require('../../../.storybook/main');
module.exports = {
...rootMain,
core: { ...rootMain.core, builder: 'webpack5' },
stories: [
...rootMain.stories,
'../src/lib/**/*.stories.mdx',
'../src/lib/**/*.stories.#(js|jsx|ts|tsx)',
],
addons: [...rootMain.addons, '#nrwl/react/plugins/storybook'],
staticDirs: ['../../../public'],
webpackFinal: async (config, { configType }) => {
// apply any global webpack configs that might have been specified in .storybook/main.js
if (rootMain.webpackFinal) {
config = await rootMain.webpackFinal(config, { configType });
}
//Font added using url-loader
config.module.rules.push({
test: /\.(ttf|eot|woff|woff2)$/,
loader: require.resolve('url-loader'),
options: {
name: '[name].[hash].[ext]',
},
});
// add your own webpack tweaks if needed
return config;
},
};
On my project I use: TailwindCSS + Emotion + Tailwind Macro.
I just want to export TailwindCSS CSS rules to the currently generated styles.css file instead of rendering CSS rules on the DOM (html > head > style[]).
That way I would reduce the size of the app.js bundle, and of course, it will increase the size of the generated file: styles.cssbut I'm fine with that.
Any idea on how to do that?
Thanks!
Here are the config files:
tailwind.config.js
const defaultTheme = require('tailwindcss/defaultTheme');
const { darken } = require('polished');
const colors = require('tailwindcss/colors');
const theme = require(`./offers/${process.env.OFFER}/theme`);
module.exports = {
/**
* Useful for arbitrary values:
* https://tailwindcss.com/docs/adding-custom-styles#using-arbitrary-values
*/
content: [ './resources/assets/js/components/**/*.js' ],
theme: {
colors: {
transparent: 'transparent',
current: 'currentColor',
black: colors.black,
white: colors.white,
gray: colors.gray,
emerald: colors.emerald,
indigo: colors.indigo,
yellow: colors.yellow,
/**
* Palette Colors from XD Mockup.
*/
light: theme.color.light,
neutral: theme.color.neutral,
highlight: theme.color.highlight,
textMedium: theme.color.textMedium,
textMediumHover: darken(0.10, theme.color.textMedium),
textDark: theme.color.textDark,
focusPrimary: theme.color.focusPrimary,
focusPrimaryHover: darken(0.10, theme.color.focusPrimary),
focusPrimaryLight: theme.color.focusPrimaryLight,
focusSecondary: theme.color.focusSecondary,
focusSecondaryHover: darken(0.10, theme.color.focusSecondary),
focusSecondaryMed: theme.color.focusSecondaryMed,
},
fontFamily: {
nunito: ['Nunito', 'sans-serif'],
opensans: ['Open Sans', 'sans-serif'],
},
fontSize: {
...defaultTheme.fontSize,
'h1d': '40px',
'h1m': '28px',
'h2d': '28px',
'h2m': '24px',
},
screens: {
'sm': '640px', // => #media (min-width: 640px) { ... }
'md': '768px', // => #media (min-width: 768px) { ... }
'lg': '1024px', // => #media (min-width: 1024px) { ... }
'xl': '1280px', // => #media (min-width: 1280px) { ... }
'2xl': '1536px', // => #media (min-width: 1536px) { ... }
},
},
};
webpack.mix.js
const mix = require('laravel-mix');
const path = require('path');
const theme = require(`./offers/${process.env.OFFER}/theme.js`);
const jsConfig = require('./resources/scripts/js-config');
require("mix-tailwindcss");
/**
* Prevent generating files: *.LICENSE.txt
*/
mix.options({
terser: {
extractComments: false,
terserOptions: {
output: {
comments: false,
},
},
},
});
/**
* Reference:
* https://laravel-mix.com/docs/6.0/extending-mix
*/
mix.extend('addWebpackLoaders', (webpackConfig, loaderRules) => {
loaderRules.forEach((loaderRule) => {
webpackConfig.module.rules.push(loaderRule);
});
});
mix.addWebpackLoaders([
{
test: /\.less$/,
use: [
{
loader: 'less-loader',
},
{
loader: 'text-transform-loader',
options: {
prependText:
`#offer: '${process.env.OFFER}';\n` +
`#cdn-url: '${process.env.CDN_URL}';\n`,
},
},
],
},
]);
mix.webpackConfig(webpack => {
return {
output: {
path: path.resolve(__dirname, 'public/cdn'),
publicPath: process.env.CDN_URL + '/',
chunkFilename: 'js/[name].js?v=##BUILD_NUMBER##',
},
plugins: [
new webpack.DefinePlugin({
'process.env.APP_ENV': `'${process.env.APP_ENV}'`,
'process.env.THEME': JSON.stringify(theme),
'process.env.JS_CONFIG': JSON.stringify(jsConfig(process.env.OFFER)),
}),
]
};
});
const package = require('./package.json');
const deps = Object.keys(package.dependencies);
mix.extract(deps);
mix.js('resources/assets/js/app.js', 'js');
mix
.less('resources/assets/less/styles.less', 'css')
.tailwind("./tailwind.config.js");
back when tailwindcss was new this is how CSS was generated, now it has built into webpack so I dont even use this now days.
npx tailwindcss build src/styles/index.css -o src/styles/output.css
note this will generate all css and file size can go up to Mbs.
so I recommend using postcss purgecss plugin along with this.
I'm building a component library to sit alongside some react apps within a monorepo, library is currently being consumed by app1 and displaying components exported from the library just fine.
The library is based on masterial ui and themed. I'm using storybook and rollup for documentation and bundling, which means having to cater to both webpack (for storybook) and rollup for the actual library build.
.storybook/main.js:
module.exports = {
stories: ['../src/**/**/*.stories.mdx', '../src/**/**/*.stories.tsx'],
addons: [
'#storybook/addon-links',
'#storybook/addon-essentials',
'storybook-addon-designs'
],
webpackFinal: (config) => {
// Default rule for images /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/
const fileLoaderRule = config.module.rules.find(
(rule) => rule.test && rule.test.test('.svg')
)
fileLoaderRule.exclude = /\.svg$/
config.module.rules.push(
{
test: /\.svg$/,
enforce: 'pre',
loader: require.resolve('#svgr/webpack')
},
{
test: /\.(woff|woff2|eot)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: require.resolve('file-loader'),
options: {
// Limit at 50k. Above that it emits separate files
limit: 50000,
// Output below fonts directory
name: '[name].[ext]',
outputPath: '../src/build/fonts/'
}
}
]
}
)
return config
}
}
And my rollup.config.js:
import commonjs from '#rollup/plugin-commonjs'
import resolve from '#rollup/plugin-node-resolve'
import peerDepsExternal from 'rollup-plugin-peer-deps-external'
import typescript from 'rollup-plugin-typescript2'
import svgImport from 'rollup-plugin-svg-hyperscript'
import url from '#rollup/plugin-url'
import packageJson from './package.json'
export default {
input: './src/index.ts',
output: [
{
file: packageJson.main,
format: 'cjs',
sourcemap: true
},
{
file: packageJson.module,
format: 'esm',
sourcemap: true
}
],
plugins: [
peerDepsExternal(),
resolve(),
commonjs(),
typescript(),
svgImport(),
url({
include: ['**/*.woff', '**/*.eot'],
name: '[name].[ext]'
})
]
}
It should be noted that I have an Icon component that works with SVGS being bundled with rollup and displaying in storybook perfectly.
Font index:
import soehneWebKraftigWoff from './soehneWebKraftig.woff'
import soehneWebKraftigEot from './soehneWebKraftig.eot'
import soehneWebLeichtWoff from './soehneWebLeicht.woff'
import soehneWebLeichtEot from './soehneWebLeicht.eot'
export const fonts = {
soehneWebKraftigWoff,
soehneWebKraftigEot,
soehneWebLeichtEot,
soehneWebLeichtWoff
}
MuiCssBaseline:
import { fonts } from '../../fonts'
export const soehneWebKraftig = {
fontFamily: 'SoehneWebKraftig',
fontStyle: 'normal',
fontWeight: 400,
src: `url(${fonts.soehneWebKraftigWoff}) format('woff')`
}
export const MuiCssBaseline = {
'#global': {
'#font-face': [soehneWebKraftig]
}
}
The override is being indexed and exported from a folder called 'overrides' so this is my theme file:
import { createTheme } from '../../themeUtility'
import { rootPalette } from './rootPalette'
import { rootTypography } from './rootTypography'
import * as overrides from './overrides'
export const rootTheme = createTheme({
palette: rootPalette,
typography: rootTypography,
overrides: {
...overrides
}
})
The font is not loaded correctly in either storybook or in the rollup bundle (I can't see the font displaying correctly in app1 which consumes the bundle). I can see my font files renamed with hashes appearing in the rollup build output directory.
Any ideas?
Edit:
Fonts within build (but not inside the fonts folder?)
what is the best way to load global styles and images for Storybook in NextJS?
Below are the following Storybook files (config.js and scs-loader.scss and webpack.config.js)
webpack.config.js
module.exports = ({ config }) => {
config.module.rules.push({
test: /\.(ts|tsx)$/,
loader: require.resolve('babel-loader'),
options: {
presets: [require.resolve('babel-preset-react-app')],
},
});
config.resolve.extensions.push('.ts', '.tsx');
config.module.rules.push({
test: /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/,
loader: require.resolve('file-loader')
});
return config;
};
config.js
import '!file-loader!style-loader!css-loader!sass-loader!./scss-loader.scss';
import { configure } from '#storybook/react';
configure(require.context('../components', true, /\.stories\.(j|t)sx?$/), module);
scss.loader.scss
#import '../styles/global.scss';
Using the above config I can get a successful Storybook build however I do not see any global styles nor do I see any images referenced in global.scss
If i remove the file-loader in config.js as follows
import '!style-loader!css-loader!sass-loader!./scss-loader.scss';
import { configure } from '#storybook/react';
configure(require.context('../components', true, /\.stories\.(j|t)sx?$/), module);
I see this error.
Error: Can't resolve '/images/hero.jpg' in 'D:\dev\personal\nextjs-ts\.storybook'
If I then go and remove all the images from global.scss I can then load Storybook and I can see the global styles however I've lost all the images. I need to try and get images and global styles working in tandem. Thanks.
Here is an example of how the images are referenced in global.scss.
.hero {
width: 100%;
height: 800px;
background: url('/images/hero.jpg');
background-repeat: no-repeat;
background-position: center;
background-size: cover;
}
It throws DOM EXCEPTION
styles file
import { fontUrls1 } from './Fonts/Amaranth-Bold.ttf';
const GlobalStyle = createGlobalStyle`
#font-face {
font-family: 'Amaranth-Bold';
src: url('${fontUrls1}') format('truetype');
}
body {
font-family: 'Amaranth-Bold';
}
body.fontLoaded {
font-family: 'Amaranth-Bold' ;
}'
My app.js file
const openUberMoveLightObserver = new FontFaceObserver('Amaranth-
Bold',{});
Executing this line throws an error.
openUberMoveLightObserver.load().then(() => {
document.body.classList.add('fontLoaded');
});
Uncaught (in promise) DOMException
You don't need to import fonts to add into styles....
You can do this just passing url to the fonts
like this.....
export const MyFonts = createGlobalStyle`
#font-face {
font-family: 'Metropolis';
src: url(/static/fonts/Metropolis-ExtraLight.ttf);
src: url(/static/fonts/Metropolis-Light.ttf);
src: url(/static/fonts/Metropolis-LightItalic.ttf);
src: url(/static/fonts/Metropolis-Medium.ttf);
src: url(/static/fonts/Metropolis-Regular.ttf);
src: url(/static/fonts/Metropolis-SemiBold.ttf);
}
`;
OR
export const MyFonts = createGlobalStyle`
#import url('/static/fonts/Metropolis-ExtraLight.ttf');
#import url('/static/fonts/Metropolis-Light.ttf');
#import url('/static/fonts/Metropolis-LightItalic.ttf');
#import url('/static/fonts/Metropolis-Medium.ttf');
#import url('/static/fonts/Metropolis-Regular.ttf');
#import url('/static/fonts/Metropolis-SemiBold.ttf');
`;
you can use this font by importing inside your component
import React from 'react';
import MyFonts from './MyFonts';
function Example(){
return (
<div>
<MyFonts />
....
</div>
)
}
export default Example
You need to add a specific loader to load the font. For example if you are using webpack then you can use url-loader as below:
{
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
exclude: path.resolve(__dirname, "node_modules"),
use: [
{
loader: "url-loader",
options: {
prefix: "font",
limit: 10000,
mimetype: "application/octet-stream"
}
}
] }