How to add custom local fonts to a Nextjs 13 Tailwind project? - next.js

I have downloaded a couple fonts (NOT GOOGLE FONTS) and I want to add and use them in my Nextjs 13 Tailwind project.
I've followed the Nextjs docs to try add a single font (I want to add multiple fonts but trying to get a single font added isn't working):
npm install #next/font
Add the downloaded font files to /pages/fonts
Add the fonts to /pages/_app.js
Add the fonts to tailwind.config.js
Use font in a component
Updated /pages/_app.js
import localFont from '#next/font/local'
const surt = localFont({
src: './fonts/Surt-Normal-Bold.woff2',
variable: '--font-surt-bold',
})
export default function MyApp({ Component, pageProps }) {
return (
<main className={surt.variable}>
<Component {...pageProps} />
</main>
)
}
Updated tailwind.config.js (Surt is a sans-serif font)
const { fontFamily } = require('tailwindcss/defaultTheme')
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {
fontFamily: {
sans: ['var(--font-surt)', ...fontFamily.sans],
},
},
},
plugins: [],
}
Updated About page
export default function About() {
return <div className="font-surt-bold">About</div>
}
What am I doing wrong and how would I update the code to add another font (eg Surt-Normal-Regular.woff2, Surt-Normal-Semibold-Italic.woff2)

After setting up TailwindCSS the way you did to use the font you should also add font-sans in the className you want to add the font to.
In this case, your _app.js should be
import localFont from '#next/font/local'
const surt = localFont({
src: './fonts/Surt-Normal-Bold.woff2',
variable: '--font-surt-bold',
})
export default function MyApp({ Component, pageProps }) {
return (
<main className={`${surt.variable} font-sans`}>
<Component {...pageProps} />
</main>
)
}
If you want to have different variants of the same font you can pass an array to the font src instead.
You can do it this way
const surt = localFont({
src: [
{
path: "./fonts/Surt-Normal-Regular.woff2",
weight: "400",
style: "normal",
},
{
path: "./fonts/Surt-Normal-Bold.woff2",
weight: "700",
style: "normal",
},
{
path: "./fonts/Surt-Normal-Black.woff2",
weight: "900",
style: "normal",
},
],
variable: "--font-surt-bold",
});
it is mentioned in the docs Here

Full credit goes to Lee Robinson and official v13 doc for my post.
The below will also help you with the new v13 /app directory.
1. Install next fonts
npm install #next/font
2. Download your fonts
In my case I downloaded the Poppins fonts and placed them in /public/fonts/
3. Link to your fonts file
As per docs you can edit your _app.js.
Link to your local fonts
assign a variable name
assign the class name to a parent html tag
In my case I am using the new app directory: /src/app/layout.tsx
import localFont from '#next/font/local'
const poppins = localFont({
src: [
{
path: '../../public/fonts/Poppins-Regular.ttf',
weight: '400'
},
{
path: '../../public/fonts/Poppins-Bold.ttf',
weight: '700'
}
],
variable: '--font-poppins'
})
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={`${poppins.variable} font-sans`}>
...
4. Reference in Tailwind config
As per docs you can now reference the new variable in your tailwind.config.js.
/** #type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/app/**/*.{js,ts,jsx,tsx}', // Note the addition of the `app` directory.
'./src/pages/**/*.{js,ts,jsx,tsx}',
'./src/components/**/*.{js,ts,jsx,tsx}'
],
theme: {
extend: {
fontFamily: {
sans: ['var(--font-poppins)']
}
}
},
plugins: []
}

If you console.log(surt),
const surt = localFont({
src: "../fonts/test.woff2",
variable: "--font-test-bold",
});
console.log("surt", surt);
you get this
// I used different font so values might be different
surt {
style: { fontFamily: "'__surt_899d56', '__surt_Fallback_899d56'" },
className: '__className_899d56',
variable: '__variable_899d56'
}
You dont need any configuration. All you have to do is apply this surt.className to an element and that font will be applied to all children.
<main className={surt.className}>
<Component {...pageProps} />
</main>
It works both for client components and app directory
how to apply this to any component in the project
I did the above configuration in _app.js and I did use any className or variable
import localFont from "#next/font/local";
const surt = localFont({
src: "../public/test.woff2",
variable: "--font-surt",
// I added this maybe fallback works but it did not
fallback: ["'Apple Color Emoji'"],
});
console.log("surt", surt);
export default function MyApp({ Component, pageProps }) {
return (
<main>
<Component {...pageProps} />
</main>
);
}
After you configure the tailwind.css the way you did, we should be able to use font-sans class anywhere in the project but no matter what I tried, tailwind does not inject it (it must be a bug). Workaround is like this. If you console the font, you will get this:
className: "__className_899d56"
style: {fontFamily: "'__surt_899d56', 'Apple Color Emoji','__surt_Fallback_899d56'"}
variable: "__variable_899d56"
I copied the style property from console (You could prop drill) and manually used it:
<p
className="'__className_899d56'"
style={{ fontFamily: "__surt_899d56" }}
>
with font testing
</p>

Related

Error while trying to add external local fonts in nextJS

I'm trying to add local font to my NextJS + TailwindCSS project.
I have added the font inside public/fonts folder and I'm following the docs:
This is my code
import localFont from '#next/font/local'
const inter = Inter({
subsets: ['latin'],
})
const recoleta = localFont({
src: 'fonts/Recoleta-Regular.ttf',
fontFamily: 'Recoleta',
})
And I'm getting this error from the terminal.
I need help on which folder to add it or how to configure it perfectly.
Module not found: Can't resolve './fonts/Recoleta-Regular.ttf'
Had this error and fixed the issue by setting it up as such. Used https://nextjs.org/docs/api-reference/next/font#src for assistance.
Using app folder.
page.tsx:
import CustomFont from '#next/font/local'
const cfont = CustomFont({
src: '../public/fonts/cfont.ttf',
variable: '--font-cfont',
})
export default function Home() {
return (
<div className={`${cfont.variable}`}>
<div className="font-cfont">
Test
</div>
</div>
)
}
tailwind.config.js:
const { fontFamily } = require('tailwindcss/defaultTheme')
/** #type {import('tailwindcss').Config} */
module.exports = {
content: [
"./app/**/*.{js,ts,jsx,tsx}",
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {
fontFamily: {
cfont: ['var(--font-cfont)', ...fontFamily.sans],
},
},
},
plugins: [],
}

#aws-amplify/ui-react how to customize UI if my project uses SASS?

I'm extending a Next.js (React) project that was built by someone else, which uses .scss files (SASS) for styling. This is the project in question https://github.com/codebushi/nextjs-starter-dimension
Now I'm adding an authentication flow to the project using #aws-amplify/ui-react. Everything works fine, but I want to customize the UI style. I've found in the documentation that I can do that through :root in globals.css, as so:
:root {
--amplify-primary-color: #ff6347;
--amplify-primary-tint: #ff7359;
--amplify-primary-shade: #e0573e;
}
Documentation here: https://docs.amplify.aws/ui/customization/theming/q/framework/react
I know pretty much nothing about SASS except the basics. How would I do the equivalent of setting those variables in :root?
Edit with more details
This is my next.config.js:
module.exports = {
webpack: (config, { dev }) => {
config.module.rules.push(
{
test: /\.scss$/,
use: ['raw-loader', 'sass-loader']
}
)
return config
}
}
This is my authentication page where the Amplify elements are defined:
import { useState, useEffect } from 'react'
import { AuthState, onAuthUIStateChange } from '#aws-amplify/ui-components';
import { AmplifyAuthenticator, AmplifyAuthContainer, AmplifySignUp, AmplifySignIn, AmplifySignOut } from '#aws-amplify/ui-react';
import Router from 'next/router'
const Auth = (props) => {
const [authState, setAuthState] = useState();
const [user, setUser] = useState();
useEffect(() => {
return onAuthUIStateChange((nextAuthState, authData) => {
setAuthState(nextAuthState);
setUser(authData);
//console.log(`authData: ${JSON.stringify(authData, null, 2)}`);
});
}, []);
if (authState === AuthState.SignedIn && user) {
Router.push('https://mywebsite')
return <p>Redirecting...</p>
}
return (
<AmplifyAuthContainer>
<AmplifyAuthenticator usernameAlias="email">
<AmplifySignUp
slot="sign-up"
usernameAlias="email"
formFields={[
{
type: "email",
label: "Email Address *",
placeholder: "Enter your email address",
inputProps: { required: true },
},
{
type: "password",
label: "Password *",
placeholder: "Enter your password",
inputProps: { required: true },
},
]}
/>
<AmplifySignIn slot="sign-in" usernameAlias="email" />
</AmplifyAuthenticator>
</AmplifyAuthContainer>
);
}
export default Auth;
As per the answer below from Sean W, I've already tried creating an _app.js with:
import '../styles/global.scss'
const App = ({ Component, pageProps }) => {
return <Component {...pageProps} />
}
export default App;
with global.scss:
:root {
--amplify-primary-color: #ff6347;
--amplify-primary-tint: #ff7359;
--amplify-primary-shade: #e0573e;
}
But the CSS variables don't seem to be replaced.
Include the styles in your pages. The easiest way is to create a new SCSS file and include it in a custom _app.js.
By default, Next supports SASS - you only need to install the sass npm package and likely do not need a custom next.config. This could be one of your problems.
file - global.scss -
:root{
--amplify-primary-color: #ff6347;
--amplify-primary-shade: #e0573e;
}
//scoped & redeclared to an element with id #__next
:root #__next {
--amplify-primary-color: #ff6347;
--amplify-primary-shade: #e0573e;
}
Amplify could also be setting setting styles after you have already defined them for the :root - if this is the case you will need to scope your custom styles to take precedence over the default Amplify CSS variables. To do this - redeclare them in a CSS rule that is more specific than :root like the example above - keeping order of precedence in mind
Amplify also lets you directly target components.
amplify-authenticator {
--amplify-primary-color: #ff6347;
background: var(--amplify-primary-color);
padding: 5px;
}
// scoped & redeclared
:root #__next amplify-sign-in{
--amplify-primary-color: #ff6347;
}
The amplify elements that can be targeted are
amplify-authenticator
amplify-sign-in
amplify-confirm-sign-in
amplify-sign-up
amplify-confirm-sign-up
amplify-forgot-password
amplify-require-new-password
amplify-verify-contact
amplify-totp-setup
import your file - pages/_app.js
import 'path/to/global.scss';
const App = ({ Component, pageProps }) => <Component {...pageProps} />;
export default App;
Global variables (the equivalent of :root variables) can be created simply with this line.
$color = #c0ff33 !important;
The content can be anything that is available in css and partially sass.
For large projects, creating a variables.scss file and including in it all these variable declarations can be truly helpful for changing multiple variables at once. To include this file, just do #import "./variables.scss" at the top of your file.
Hope this helps! Good luck in your sassy endeavors!

Font is not loading when using styled-components with react and storybook

I'm building app based on React, Typescript with build on Webpack. So basically everything is working ok when using webpack or webpack-dev-server. But then I wanted to use storybook, and I want to put some fonts that are inside my project directory. And in sotrybook I can't see my fonts, they're not working for some reason - I think that could be webpack related.
I'm using this to load fonts:
//globalStyle.ts
import { createGlobalStyle } from "styled-components";
import bender from "../resources/fonts/Bender.otf";
const GlobalStyle = createGlobalStyle`
#font-face {
font-family: 'Bender';
src: url(${bender});
font-weight: 400;
font-style: normal;
}
So using this in code as <GlobalStyle/> with default webpack build is working. But to apply the effect for each story-book component I use this decorator:
import * as React from "react";
import GlobalStyle from "../src/styles/globalStyle";
import mainTheme from "../src/styles/mainTheme";
import { ThemeProvider } from "styled-components";
import { configure, addDecorator } from "#storybook/react";
const req = require.context("../src/", true, /\.stories\.tsx$/);
function loadStories() {
req.keys().forEach(filename => req(filename));
}
const withGlobal = storyFn => {
return (
<ThemeProvider theme={mainTheme}>
<GlobalStyle />
{storyFn()}
</ThemeProvider>
);
};
addDecorator(withGlobal);
configure(loadStories, module);
Going to the story-book inspector I see that style has loaded, but looking at #font-face fields i see something like
#font-face {
src: url()
/**/
}
So the URL of font is empty. I tried replacing in my code this import to require(_path_) and the src: url() was filled but then no file under this address.
I am using custom storybook webpack config:
const path = require('path');
module.exports = {
stories: ['../src/**/*.stories.tsx'],
addons: ['#storybook/preset-typescript'],
webpackFinal: async config => {
config.module.rules.push(
{
test: /\.(ts|tsx)$/,
use:
[
{ loader: require.resolve('ts-loader') },
{ loader: require.resolve('react-docgen-typescript-loader') },
],
},
{
test: /\.(png|woff|woff2|eot|ttf|svg|otf)$/,
use: [
{
loader: require.resolve('url-loader'),
},
],
},
);
config.output.publicPath = "http://localhost:9001/"
config.resolve.extensions.push('.js', '.jsx', '.ts', '.tsx');
return config;
},
}
I had a similar issue because my font wasn't being downloaded so I added the font link to a file preview-head.html inside .storybook folder.
Check https://storybook.js.org/docs/configurations/add-custom-head-tags/

PurgeCSS does not remove unused CSS from NextJS project

I'm trying to remove unused css from my NextJS project using PurgeCSS. However, I'm having difficulty getting the most basic integration of PurgeCSS into my project to work.
I'm using this documentation: https://www.purgecss.com/guides/next.
My next.config file looks like this:
// next.config.js
const withCss = require('#zeit/next-css')
const withPurgeCss = require('next-purgecss')
module.exports = withCss(withPurgeCss())
Here is my component:
import React from 'react'
import App from 'next/app'
export default class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return (
<>
<style jsx global>{`
.purgecss-test {
color: red;
}
`}</style>
<Component {...pageProps} />
</>
)
}
}
When I do a global search in my code base for 'purgecss-test', I only get the one result that's in the component above, so I am expecting that style to be removed during the build. However, when my app builds and I navigate to the page and inspect the source, I can still see it there:
<style amp-custom="">.purgecss-test{color:red;}
Try customizing PostCSS instead as per this page (ignore tailwindcss): https://nextjs.org/learn/basics/assets-metadata-css/styling-tips
A bit simplified, install #fullhuman/postcss-purgecss and postcss-preset-env, then create a postcss.config.js file in the top-level directory and enter this in it:
module.exports = {
plugins: [
[
'#fullhuman/postcss-purgecss',
{
content: [
'./pages/**/*.{js,jsx,ts,tsx}',
'./components/**/*.{js,jsx,ts,tsx}'
],
defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
}
],
'postcss-preset-env'
]
};

using google fonts in nextjs with sass, css and semantic ui react

I have a next.config.js file that has the following configuration:
const withSass = require('#zeit/next-sass');
const withCss = require('#zeit/next-css');
module.exports = withSass(withCss({
webpack (config) {
config.module.rules.push({
test: /\.(png|svg|eot|otf|ttf|woff|woff2)$/,
use: {
loader: 'url-loader',
options: {
limit: 100000,
publicPath: './',
outputPath: 'static/',
name: '[name].[ext]'
}
}
})
return config
}
}));
This is great because it runs my semantic ui css files.
Now I have a problem. I can't seem to successfully import any google font url. I tried downloading the ttf file into my file path and tried to reference it with the #import scss function. However I get a GET http://localhost:3000/fonts/Cabin/Cabin-Regular.ttf net::ERR_ABORTED 404 (Not Found) error
Here is what I'm trying to do with google font:
#font-face {
font-family: 'Cabin';
src: url('/fonts/Cabin/Cabin-Regular.ttf') format('truetype');
}
$font-size: 100px;
.example {
font-size: $font-size;
font-family: 'Cabin', sans-serif;
}
I have also downloaded the relevant npm dependencies:
"#zeit/next-css": "^1.0.1",
"#zeit/next-sass": "^1.0.1",
"file-loader": "^2.0.0",
"next": "^7.0.2",
"node-sass": "^4.9.4",
"react": "^16.6.0",
"react-dom": "^16.6.0",
"semantic-ui-css": "^2.4.1",
"semantic-ui-react": "^0.83.0",
"url-loader": "^1.1.2"
I know in Next.js 9.3, you can copy the #import statement from Google Fonts:
#import url('https://fonts.googleapis.com/css2?family=Jost&display=swap');
and place this in some css file, lets say styles/fonts.css like so:
#import url('https://fonts.googleapis.com/css2?family=Jost&display=swap');
.jost {
font-family: 'Jost', sans-serif;
}
Then import that inside of your global _app.js file like so:
import `../styles/fonts.css`
Now you have global access to that class containing the Google Font in every next.js page
I think the other solution is to use fonts directly from Google. Just customize _app.js file and add a <link rel="stylesheet" /> in the <Head />
Example _app.js
import React from 'react';
import App, { Container } from 'next/app';
import Head from 'next/head';
export default class MyApp extends App {
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return { pageProps };
}
render() {
const { Component, pageProps } = this.props;
return (
<Container>
<Head>
<link
href="https://fonts.googleapis.com/css?family=Cabin"
rel="stylesheet"
key="google-font-cabin"
/>
</Head>
<Component {...pageProps} />
<style global jsx>{`
body {
font-family: 'Cabin', sans-serif;
}
`}</style>
</Container>
);
}
}
class NextApp extends App {
render() {
const { Component } = this.props
return (
<React.Fragment>
<Component {...pageProps} />
<style jsx="true" global>{`
#import url('https://fonts.googleapis.com/css?family=Roboto');
body {
margin: 0;
font-family: 'Roboto', sans-serif;
}
`}</style>
</React.Fragment>
</Provider>
</Container>
)
}
}
Including the font url from Google Fonts in styled-jsx worked for me.
As per the latest docs you can now add global css by updating the _app.js file and importing your css style. Follow the steps below
Create custom _app.js file in the pages directory by following the docs.
Add your styles.css file to the pages directory.
Include the styles as below
// _app.js
// Import styles
import './styles.css'
// Function to create custom app
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
// Export app
export default MyApp
and done. These styles styles.css will apply to all pages and components in your application. Due to the global nature of stylesheets, and to avoid conflicts, you may only import them inside _app.js.
I had to put the files into the static folder for it to work, must've been a specific setup for rendering images and fonts in nextjs
If you are using a functional custom app, then you can add google fonts or any cdn linked fonts to the head of the whole app as follows:
import Head from 'next/head';
// Custom app as a functional component
function MyApp({ Component, pageProps }) {
return (
<>
<Head>
<link
href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght#300;400;600;700&display=swap" rel="stylesheet"/>
</Head>
<Component {...pageProps}/>
</>
)
}
MyApp.getInitialProps = async ({ Component, ctx }) => {
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return { pageProps };
};
// Export app
export default MyApp
now you can use css to apply the font family to the body element to get the font applied throughout the webiste, as shown below.
body {
font-family: 'Source Sans Pro', sans-serif;
}
Hhis is now how I am currently loading external fonts nonblocking. In _document.js head:
<script dangerouslySetInnerHTML={{__html: '</script><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" media="print" onload="this.media=\'all\'" /><script>' }} />
dangerouslySetInnerHTML and some script hacking to work around onload otherwise being removed from _document.js until this is resolved
source

Resources