I'm migrating a create-react-app to nextjs and am learning the ups and downs for the first time. Nextjs 13 introduces next/font and it seems, as I'm learning nextjs for the first time, a good idea to use it in my workflow rather than learning methods that be deprecated.
Desire
I want to use several Google Fonts and be able to reuse them across the app dynamically. As in, I could create a single file:
fonts.js
import { Roboto, Poppins } from '#next/font/google';
export const roboto = Roboto({
weight: '400',
subsets: ['latin'],
variable: '--roboto-default'
});
export const poppins = Poppins({
weight: '400',
subsets: ['latin'],
variable: '--poppins-default'
});
And reference this in any component in such a way that if I change the const name, or any parameters, all affected components will change.
My previous experiences / familiarity
In react-create-app I used sass globals in external stylesheets. I could import google fonts the standard way (#import), in a stylesheet and create a variable like so:
globalfonts.sass
$font01: 'Roboto, arial, sans-serif';
Components throughout the app could have their own stylesheets that referenced:
import globalfonts.sass
.componentname {
font-family: $font01
}
And if I changed the value of $font01, all components would adjust. This made it very easy to sketch out changes. I could create a similar $font01--size variable too, etc.
Question
I'm looking for a similar functionality in nextjs13 using next/font as mentioned above in previous experiences, via a stylesheet.
I've created an app/fonts.js file and I import it in app/layout.js in the app directory but I'm a bit lost as to what to do next as documentation still seems to reference the older method of using the pages directory, which appears to be being phased out.
I can import a const from fonts.js into each component and reference them inline as constname.className. But this obfuscates things by splitting my styling between stylesheets and inline font references. If I want to change font configs across the entire app quickly, it feels so much more straightforward to use sass globals.
Follow up question:
As I feel like very few references to next/font online are mentioning stylesheets, I feel I need to ask: Are stylesheets dying out? Is styling each component inline on a component-by-component basis becoming more popular?
Attempt
If I try the following in app/layout.jsx
import { roboto, poppins } from './fonts';
import styles from './globals.scss'
export default function RootLayout({ children }) {
return (
<html lang="en">
{/*
<head /> will contain the components returned by the nearest parent
head.jsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
*/}
<head />
<body className={roboto.className}>{children}</body>
</html>
)
}
The roboto font is not used. I'm unsure as to what I've done wrong.
And if I go and use className={roboto.className} on every single component I want to use this font for (some examples seem to suggest this method), it feels like I'm shooting myself in the foot if I want to change that font in the future. I'll have lost the single reference point I was using in my old method with having $font01
Related
We write css when we use styled components, and I dont understand the benefit of using it why we just dont use the css we write as a classname for the components. In styled components we have wrappers of the components and when we use plain css we have class names. what is the advantage of wrapping components with styled components
The reason styled components is pushed in React web development is to encapsulate styles to only those components. There is no risk of bleeding css into other components. Rather than learn a new css organisation structure new developers can onboard more quickly.
That being said, it is restrictive, slower to develop with, and adds some page weight by applying same styles you could have shared.
I have found the fastest way to work is create static html webpages with pure css, and organise it in a way you are going to import it into your framework. Then you can have boilerplate html designs that can be tested independently of the compiler, which is so much faster to test in Internet Explorer, Firefox, chrome, mobile devices and all the varying screen sizes.
If you want to use styled components, you can either use this npm plugin to convert your static css into variables to use with style components -
https://www.npmjs.com/package/#inspiraller/create-css-vars
or just don't. Nextjs does not support css imports unless its global, so webpack compiling option is a more universal solution.
Main benefits of styled-components are listed in the Motivations page on their website.
styled-components is the result of wondering how we could enhance CSS for styling React component systems. By focusing on a single use case we managed to optimize the experience for developers as well as the output for end users.
Apart from the improved experience for developers, styled-components provides:
Automatic critical CSS: styled-components keeps track of which components are rendered on a page and injects their styles and nothing else, fully automatically. Combined with code splitting, this means your users load the least amount of code necessary.
No class name bugs: styled-components generates unique class names for your styles. You never have to worry about duplication, overlap or misspellings.
Easier deletion of CSS: it can be hard to know whether a class name is used somewhere in your codebase. styled-components makes it obvious, as every bit of styling is tied to a specific component. If the component is unused (which tooling can detect) and gets deleted, all its styles get deleted with it.
Simple dynamic styling: adapting the styling of a component based on its props or a global theme is simple and intuitive without having to manually manage dozens of classes.
Painless maintenance: you never have to hunt across different files to find the styling affecting your component, so maintenance is a piece of cake no matter how big your codebase is.
Automatic vendor prefixing: write your CSS to the current standard and let styled-components handle the rest.
With styled components it's possible to "enhance" CSS classes with JavaScript code because are written in JS.
const Input = styled.input`
color: ${props => props.hasError ? 'red' : 'black'};
`;
Opposed to writing 2 separate CSS classes and control which to show with JS.
.text {
color: black;
}
.text--danger {
color: red;
}
<input className={`text ${hasError ? 'text--danger' : ''}`} />
On the long run it can produce more maintainable code, but it's a matter of personal opinion if it's better to use it or not.
With styled components you can add logic programming
I have read a few different sources about this and according to the official docs:
Consequently, there are a bunch of practical issues to consider with
traditional CSS-file-based styling (especially with SSR), so we
suggest avoiding this method when styling for Next.js.
Instead we recommend CSS in JS, which you can use to style individual
components rather than importing CSS files.
Next.js comes preloaded with a CSS in JS framework called styled-jsx,
specifically designed to make your life easier. It allows you to write
familiar CSS rules for your components; rules will have no impact on
anything other than the components (not even child components).
But I have seen tutorials out there showing that you can load in css files like:
import Head from "next/head";
const Layout = props => (
<>
<Head>
<link href="theme.css" rel="stylesheet" />
</Head>
<Header />
<main>{props.children}</main>
</>
);
This would make my life much easier because I am using an html theme which I am porting to a next.js project. And the theme.css file is 23000 lines of code long. Imagine now I had to try and find all those css classes, extract them and put them into their individual components. That would take forever!
So, the actual question is, is it really that bad to just pull in the entire stylesheet and use it 'globally'?
I am actually trying to figure out too the best way to load css in a next js project, I decided recently to import all my pages style sheets in the _app.js but then i found that my lighthouse score went from 70% to 42%. I used always the jsx approach and i guess it is much better for better performance.
like this for example :
const ReVerifyLink = () => (
<div>
<p>
Click
<Link href={{ pathname: routes.RE_VERIFY }}>
<a>here</a>
</Link>
to re-verify your email.
</p>
<style jsx scoped>{`
a {
font-weight: bold;
color: #ed8f1f;
margin: 0 5px;
}
`}</style>
</div>
);
I have a web app built by Web app, which takes a lot of time to first loading.
After investigation- I've tried a lot of improvements, also that one of using only critical CSS on server side rendering.
but now' after SSR has finished- React generates a lot more CSS rules- which are not used at this point,
so my question is:
Is that possible to split CSS and load them on demand (i.e.: on route change)?
Any help will be appreciated.
Thanks in advance!
Try using css-modules.
/* style.css */
.className {
color: green;
}
Let's say you have the above styles file. You can use the below code to apply the styles.
import styles from "./style.css";
const ExampleComponent = () => (
<div className={styles.className}>
Hello World!
</div>
);
You should do the webpack config to make importing CSS files to work.
css-modules documentation
Dynamically imported components / Async components react loadable.
I solved the problem by using the amazing package: 'react-universal-component',
you can read about it more here and here.
You can use then the css chunks, or the way I did it-
split css files according to your components, and call them from the component itself, without using a 'wholeCss.css' file or something like this.
This way, you can be sure that no needed css was not loaded.
Webpack/CSS Experts!
My NodeJS/React application has the following structure, where I use Webpack 3.8.1 to package & bundle files.
-app
|--actions
|--constants
|--components
|--containers
|--css
|--reducers
|--store
-common
-server
-webpack
|--rules
|--css
|--javascript
|--externals
|--paths
|--resolve
|--webpack.config.js
Everything works as expected in development and production but there is one problem that I have been struggling with: External node modules CSS. When I add a new dependency, which comes with some sort of CSS/SCSS/SASS stylesheets and for that matter makes use of className to style different components, I hit FOUC CSS where none of the external node module's CSS styles has been recognized and therefore are missing from the map that webpack 3 creates once the build process is complete.
As an example, I am using Material-UI as a dependency and so far everything works but when attempting to do the following:
<IconButton>
<FontIcon className="muidocs-icon-action-home"/>
</IconButton>
... and nothing is displayed. This is just one example and I have been facing this issue with other node modules too! So I believe there is something wrong with my webpack configurations or the way I am loading CSS modules. For your reference, I have created a gist of all webpack configuration files.
I have come across with some similar questions like this but am not sure if that will help long-run as my project is becoming bigger.
Your helps & thoughts are greatly appreciated!
[Note] As a side note, and apart from external CSS modules, when I add a new CSS declaration to main.css, I still need to have something like:
import classNames from "classnames/bind";
import styles from "../../../css/components/timeline.css";
const cx = classNames.bind(styles);
within import section and later do something like this:
<div ref="container"
className={cx("calendar-style")}/>
to have CSS styles correctly applied, which again I suspect isn't optimal. As I mentioned earlier, webpack creates a hashmap of all CSS styles inside a .css file under public, whose content look something like this:
._1zvVaiwSh1X6O03wrS2gD- {
height: 100%;
padding: 0;
margin: 0;
}
._2W0FVEAQcsYxEbCbWHcCE3 {
height: 100%;
}...
Therefore, failing to use cx as stated above, will have the same problem as explained earlier and none of my CSS styles are applied. So, a following question would be: how webpack creates these hashes and why? How to control them being generated or not?
I've been building a project for a while using Webpack, Sass, and CSS modules. Normally in my .scss files, I define a class like:
:local(.button) {
color: white;
}
and in my React components, in the render method, I require the styles:
render = () => {
const styles = require('./MyStyles.scss');
<div className={ styles.button } />
}
and all is good with the world. Everything works as expected.
Now today I was reading through the CSS Modules page and noticed that none of the selectors were encompassed by :local() like mine and furthermore that they were importing the styles like:
import styles from './MyStyles.scss';
And I thought "Wow that looks much nicer, it's easier to see where it's imported, ect. And I'd love not to use :local() and just have things local by default." So I tried that and immediately ran into several problems.
1) `import styles from './MyStyles.scss';
Because I'm using ESLint on my React files, I immediately get an error thrown that MyStyles.scss doesn't have a default export which would normally make sense but the CSS Modules page stated:
When importing the CSS Module from a JS Module, it exports an object with all mappings from local names to global names.
so I naturally expected the default export of the stylesheet to be the object they're referring too.
2) I tried import { button } from './MyStyles.scss';
This passes linting but button logs as undefined.
3) If I revert to the require method of importing my styles, anything not specified with :local is undefined.
For reference, my webpack loader (I'm also including Node-Neat and Node-Bourbon, two awesome libraries):
{ test: /.(scss|css)$/, loader: 'style!css?sourceMap&localIdentName=[local]___[hash:base64:5]!resolve-url!sass?outputStyle=expanded&sourceMap&includePaths[]=' + encodeURIComponent(require('node-bourbon').includePaths) +
'&includePaths[]=' + encodeURIComponent(require('node-neat').includePaths[1]) + '&includePaths[]=' + path.resolve(__dirname, '..', 'src/client/') }
My questions, following all of this, are:
1) When using CSS Modules with Sass, am I confined to using either :local or :global?
2) Since I'm using webpack, does that also mean I can only require my styles?
Soon after posting, I figured out the solution. The problem, which I thought was quite confusing, was in my Webpack config. Originally my loader looked like:
loader: 'style!css?sourceMap&localIdentName=[local]___[hash:base64:5]!resolve-url!sass?outputStyle=expanded&sourceMap
which enabled to me to 1) require my Sass and 2) wrap my styles in :local.
However, the css loader was missing the modules option so that it looked like:
loader: 'style!css?modules&sourceMap&localIdentName=[local]___[hash:base64:5]!resolve-url!sass?outputStyle=expanded&sourceMap
Now I can import my styles and I don't have to wrap them in :local (although I presume I still can if I want to).
What I found most interesting about all this is that without the modules option, one can still use CSS Modules-esque features, although somewhat limiting.
EDIT:
Something I noticed, a future warning to whomever looks at this answer, is if you're using the eslint-plugin-import to lint the imports in your javascript code, it will throw an error upon importing styles like:
import styles from './MyStyles.scss';
because of the way CSS Modules exports the resulting styles object. That does mean you'll be required to do require('./MyStyles.scss') to bypass any warnings or errors.