styled-jsx
support, potentially more optimizedImporting CSS into JavaScript applications is a practice popularized by modern day bundlers like Webpack.
However, it's tricky to get CSS imports right: unlike JavaScript, all CSS is globally scoped. This means it does not lend itself well to bundling. Especially bundling between multiple pages where styling is separated in a CSS file per page and load order matters.
By our measurements, more than 50% of Next.js users use Webpack to bundle .css
files, either through @zeit/next-css
, @zeit/next-sass
, @zeit/next-less
, or a custom setup. This is further verified through talking to companies. Some have all their styles through importing CSS, others use it for global styles and then use a CSS-in-JS solution to style components.
Currently we have these three plugins that allow you to import CSS, however, they have common issue in CSS ordering and certain loading issues. The reason for this is that @zeit/next-css
doesn't enforce a convention for global styles. Global CSS can be imported in every component / JavaScript file in the project.
To solve these common issues and make it easier for users to import CSS we're planning to introduce built-in support for CSS imports. This will be similar to styled-jsx where if you don't use the feature there will be no built-time or runtime overhead.
This effort also allows us to improve developer experience of the solution.
Next.js should support modern CSS and down-level it on behalf of the user. This approach would be similar to how we support modern JavaScript and compile it down to ES5.
By default, we should:
During development, CSS edits should be automatically applied, similar to JavaScript hot reloading.
In production, all CSS should be fully extracted from the JavaScript bundle(s) and emitted into .css
files.
Furthermore, this .css
should be code-split (when possible) so that only the critical-path CSS is downloaded on page load.
Next.js will only allow you to import Global CSS within a custom pages/_app.js
.
This is a very important distinction (and a design flaw in other frameworks), as allowing Global CSS to be imported anywhere in your application means nothing could be code-split due to the cascading nature of CSS.
This is an intentional constraint. Because when global CSS is imported in for example, a component, it will behave different when moving from one page to another.
Usage Example
/* styles.css */
.red-text {
color: red;
}
// pages/_app.js
import '../styles.css'
export default () => <p className="red-text">Hello, with red text!</p>
Next.js will allow pure CSS Modules to be used anywhere in your application.
Component-level CSS must follow the convention of naming the CSS file .module.css
to indicate its intention to be used with the CSS Modules specification.
The :global()
CSS Modules specifier is allowed when combined with a local class name, e.g. .foo :global(.bar) { ... }
.
Usage Example
/* components/button.module.css */
.btnLarge {
padding: 2rem 1rem;
font-size: 1.25rem;
}
.foo :global(.bar) {
/* this is allowed as an escape hatch */
/* (useful when controlling 3rd party libraries) */
}
:global(.evil) {
/* this is not allowed */
}
/* components/button.js */
import { btnLarge } from './button.module.css'
// import '../styles.css'; // <-- this would be an error
export function Button({ large = false, children }) {
return (
<button
className={
large
? // Imported from `button.module.css`: a unique class name (string).
btnLarge
: ''
}
>
{children}
</button>
)
}
I'm guessing with plugins like @zeit/next-sass
already working with the current supported flavor of CSS Modules that .module.scss
would also work? Or does the phrase "pure CSS Modules" mean only .module.css
?
Sass support is make it or break it for most projects to me.
The @zeit/next-css/sass/less/stylus
plugins will be deprecated in favor of this RFC (built-in support). We're still discussing to what extent sass will be part of the core vs a plugin. My current thinking is when you add node-sass
it'll enable sass support. Similar to what we do for TypeScript. The reason we won't add it as a dependency is that it's quite large and very slow to install because of native bindings etc.
This is exciting! I was tripped by conflicting css class names just as today!
Question, how does it work with TypeScript? One reason that I haven't enabled css-module is that, importing css module would give an any object (maybe a bit magical, like .foo-bar
will be become a field called fooBar
?), which is not TS friendly. Since Next is now TS by default, I would imagine we can do better in this matter?
I'm not sure if "Automatically fix known flexbugs for the user" is going to be a good idea.
I fear that we'll end in confusing scenarios where third party component libraries on npm, that include css stylesheets, will behave differently in nextjs environments than in others (such as create-react-app).
@TheHolyWaffle fixing known flexbugs creates a more predictable behavior -- and is also what Create React App does.
@zenozen Per the CSS Modules specification, it does not do anything fancy to camelcase for you. If you use class names with a dash (class-name
) you'd need to explicitly access that on the object.
e.g.
import Everything from './file.module.css'
// later on
Everything['class-name']
As for TS, we don't plan any magic to handle the types but it's pretty easy to define a TS type to handle these files.
declare module '*.module.css' {
const classes: { readonly [key: string]: string };
export default classes;
}
declare module '*.module.scss' {
const classes: { readonly [key: string]: string };
export default classes;
}
declare module '*.module.sass' {
const classes: { readonly [key: string]: string };
export default classes;
}
Whatever we do here can we make sure it's still easy to use custom PostCSS plugins like it is now with @zeit/next-css
? It would be a real shame if we lost support for that — CRA doesn't support this and because of that it's impossible to use tools like Tailwind CSS in any sort of reasonable way.
I would be careful about these three features in this regard:
- Use Autoprefixer to add vendor-specific prefixes automatically (without legacy flexbox support)
- Polyfill or compile Stage 3+ CSS features with their equivalents
- Automatically fix known "flexbugs" for the user
...because this is all done with PostCSS, and it's very important that PostCSS plugin order be under the end-user's control.
I would suggest that if you are going to enable those three plugins by default, that the user should be able to completely override the default PostCSS config by providing their own postcss.config.js
file. They would have to add those plugins manually if they went this route, but that level of control is necessary in my opinion.
TL;DR please be careful not to break the ability for users to have complete control of PostCSS, I literally can't use CRA because of this and I would be very sad if I couldn't use Next anymore.
Seconding @adamwathan - would be great to get more detail here on how postcss option customization would look. It would be really great to have an option to modify postcss config via next plugin as well as postcss.config.js
, so that configs shared across multiple projects can be easily injected without an extra file.
I think that it is better to enable css modules for all css imports (not only for .module
suffix), and the one that we want to be global we will need to specify a .global
suffix.
CSS Modules would be great. I think keeping the *.module.(css|scss)
spec is fine as it does align with create-react-app
, allow for typescript typings, and allow for easier migrations for other users new to Next.js
With typescript, you can enforce CSS module typings by generating *.d.ts
typings for every *.(css|scss)
file using typed-css-modules and typed-scss-modules but if you don't want to do that and still want autocompletion, you can use the following VS Code extension:
https://marketplace.visualstudio.com/items?itemName=clinyong.vscode-css-modules
@adamwathan rest assured we'll be allowing PostCSS configuration! We're not sure how the _exact_ implementation will look like yet, but it should emerge naturally with the CSS pull request.
This is great!! Is there a timeline for this? Is it happening this year?
@andreisoare we're about to merge the global half of this RFC: https://github.com/zeit/next.js/pull/8710
We'll be continuing on to the CSS Modules support ASAP!
@adamwathan @Timer would be great if we could continue to use the conventional postcss.config.js file in the root of the project, as we can now with next-sass
.
So glad to see this coming to core.
Will AMP critical path rendering be supported by this RFC?
for the record, if you can, you should use styled-jsx because it allows you to encapsulate css, html and js into 1 idea, a component. If you haven't realized this already (this is a note to the users of next.js not the maintainers) this allows you to not have to buy into an entire CSS UI Kit like bootstrap, you can try 1 component and if it doesn't suit your needs, you toss it without the burden of dealing with the baggage of that external CSS dependency.
I think it's also very important to spread this knowledge because CSS is often misunderstood or an afterthought and often becomes the bane of a developers work. But with proper abstraction, it can actually be quite easy to reason about.
and just because "lots of people" are importing their CSS doesn't mean it's a great idea.
I think one of the most powerful things about react is the fact it encapsulates CSS,HTML and JS for you. and if you side-load (import) the CSS, you kind of side-step that benefit react gives you.
For the record: CSS-in-CSS(or HTML) is a much more efficient transport layer for styles, and collocating js and styles in one file is not better than collocation them in one directory.
Even more - the majority of the last generation CSS-in-JS extracts CSS to CSS at build time for the performance sake, thus proper management of a static CSS is a must. However - the way such extractions would be supported by Next was not yet disclosed.
For the record :smile:: Using SASS,LESS with a Framework like Bootstrap is still a thing and works really great! Encapsulation must not happen on JS level. Using BEM is an alternative. Supporting both ways is essential.
@timer Does the first half of this (https://github.com/zeit/next.js/pull/8710) mean path splitting of CSS now works? Or is that still to come?
(Related, https://github.com/zeit/next-plugins/issues/190)
Global CSS cannot be path split, so no. Only CSS Modules (imported at the component-level) will be path split. 👍
Is there an explanation how this affects issues #9392 and #9385? Just to understand what is happening behind the scenes. So that, I could up with a work around how to fix the issues on my codebase.
Hi. We are looking to upgrade our React app to next.js mostly to take advantage of the great tooling and server side rendering.
However for us it is a hard requirement to be able to use CSS modules. We tried with the next plugin for CSS but that wrecked havoc with problems like #9392 #9385 and more.
Is there any ETA for the CSS Modules support (even experimental)? Any way to help speed up its development?
So we can't import css at component level?
You can, you just need to use a workaround right now until the problem will be fixed. I think you can find one that works for you on the next-css issue.
Sorry for stupid question: as far as global CSS goes, how is this different/better than just including a link in the head?
@o-t-w if you import global CSS, it goes through the build pipeline with the rest of your code so it gets minimized etc then becomes part of the build and output of next.js
Depends on you if that is advantage. I imagine most people are looking forward to CSS modules so that the CSS needed for a specific component is loaded only when the component is loaded.
For context, we have a team of developers that have been using NextJS over the last two years to build almost 40 web applications for early-stage startups.
I want to take the opportunity here to outline how we style our applications to show current needs, pain points, warnings about some required npms we faced, and some concern with the proposed changes.
npm ci
to rebuild the entire node_modules
folder, clearing browser cache, the cache clear force console command all do not work as there is some global npm cache as work here. Therefore we would very much like hot reloading to work properly when updating files imported into CSS/SASS modules applied to components. In short, any SASS processing done by components should watch for changes to imports and maintain working hot reloading. This would have been solved by the webpack loader option noted in the previous bullet.We are mildly concerned with the idea of blocking the global option with component CSS/SASS modules as we have some use cases that need to, based on the component, touch the html, body, and root div tags as well as cases of generated HTML that doesn't necessary exist inside said component due to placement via portals etc. I think we could work around some of this with global SASS and some restructuring so we are not reliant on which component is loaded to apply specific global CSS.
We want full control over PostCSS and would rather not have to deal with being forced to apply a specific set of defaults.
Sorry, this was a long reply but I figure we had some insights worth sharing. Feel free to let me know if you have any questions and we are looking forward to seeing what the NextJS teams comes up with next (pun intended)!
@Soundvessel,
We are mildly concerned with the idea of blocking the global option with component CSS/SASS modules as we have some use cases that need to, based on the component, touch the html, body, and root div tags as well as cases of generated HTML that doesn't necessary exist inside said component
If I understood you correctly, you still should be able to achieve that using :global
in CSS modules.
@Daniellt1
If I understood you correctly, you still should be able to achieve that using
:global
in CSS modules.
See their note above
CSS Modules specifier is allowed when combined with a local class name
In the case of our layout component, we are targeting <html>
, <body>
and root div without a local class name.
It was also mentioned in a video on this topic that they are considering blocking the :global
option completely which doesn't make sense to me considering the large number of npms and CMS APIs that generate markup that can't be touched by scoped css modules.
Hey @Soundvessel - this is my video that I recorded internally for my team (how did you get access to this?), and does not directly represent the views of the nextjs team, nor does it guarantee up to date information since it was made during early experimental stages. Consider the RFC to be canon on this topic 😁
I would also like to thank you for proposing a solution that allows use of CSS/SASS rather than a convoluted JS in CSS solution that requires additional syntax such as camelCased properties etc. There are a great deal of us coming from a more design-focused background, having years of experience working directly with HTML/CSS, and it makes it easier for us not to have to transpose what we are seeing in code too deeply into what we are debugging on screen.
Just want to add, unless I missed some mention above, that the auto-detection/-enabling of Sass is cool, but Sass API options need to be exposed somewhere too: [email protected]
has established a good pattern including allowing the user to choose which sass engine to apply; their options could be taken over 1:1...
I would like to propose that the extension for CSS modules be configurable.
Maybe expose an array to support more than one extension other than .module.css
?
Creating a .module.css
file and importing it import "Component.module.css"
when you have many components to some developers seems a bit bloated to us.
In my case I would like to be able to create a file with extension: .m.css
, sayComponent.m.css
and import it as import "Component.m.css"
Unless the Next.js team says there are some technical difficulties that prevent this from being done.
I don't think any config for naming extensions should be opened as that seems like an unnecessary footprint increase, but I do question the _need_ for the .module.css
extension if this will be part of the implementation:
Next.js will only allow you to import Global CSS within a custom pages/_app.js.
I initially thought .module.css
would be necessary for the build to detect what should be parsed as CSS modules and what shouldn't be, but if importing non-modules in any component/page outside of _app.js
will error out and break build then the naming convention shouldn't be needed as anything imported outside _app.js
should be a CSS module by nature.
Is this being forced purely as convention rather than need?
The choice of the module.css
convention collides with the convent already established by https://github.com/css-modules/css-modules
Examples here:
https://create-react-app.dev/docs/adding-a-css-modules-stylesheet/
and here:
https://www.gatsbyjs.org/docs/css-modules/
Yes, probably making the extension configurable could complicate it more than necessary. But at least you could define .module.css
in a constant file and not hard-coded in several places to change it via monkey patching
@bySabi
The difference between this proposed implementation and create-react-app
is that importing a "regular stylesheet" like the example you linked with another-stylesheet.css
would not be allowed at all. In that instance .module.css
is used to determine which stylesheet should be handled in which manner by the build tools.
The CSS Modules spec does not establish a naming convention at all.
I know that CSS Modules does not establish any convention on the extension of the files but how the modules are imported.
Where I want to go is that .module.css
is the conventions chosen by the Next.js Teams and they should "facilitate" the possibility that some users can change it even if it is via a post-install module that doesmonkey patching
I think that .module.css
is the first naming convention | extension imposed by Next.Js. Please correct me if I'm wrong.
The choice of the
module.css
convention collides with the convent already established by css-modules/css-modules
Examples here:
create-react-app.dev/docs/adding-a-css-modules-stylesheet
and here:
gatsbyjs.org/docs/css-modulesYes, probably making the extension configurable could complicate it more than necessary. But at least you could define
.module.css
in a constant file and not hard-coded in several places to change it via monkey patching
You're contradicting your point here, .module.css
is being used by create-react-app and gatsby for the same feature. It's an established pattern.
changing the naming convention however is not an established pattern and there's no reason for allowing it in Next.js.
I think that .module.css is the first naming convention | extension imposed by Next.Js. Please correct me if I'm wrong.
Next.js establishes conventions over configuration options, eg:
pages
directory being routespages/api
being API routes.js
.tsx
.tsx
Hi @timneutkens
The pattern is in how CSS Modules
establishes how CSS files should be imported into JS objects, the name of the CSS in question is marginal. In that regard Next.js is not using that particular pattern with the new implementation.
The CSS Modules
as in Gatsby and CRA were already implemented in Next before: https://github.com/zeit/next-plugins/tree/master/packages/next-css
I don't question the convention used, .module.css
, even knowing that I could confuse CRA and Gatsby users withCSS Modules
. Choosing a suitable name is difficult and not everyone is always happy.
Although I believe that the .m.css
convention is better because it is shorter (more suitable for an extension) and less redundant (import statement is for importing modules) I would like you to take this into account. Defining conventions in general and .module.css
in specific in a constant file would help enough to patch them.
Anyway, many congratulations on the CSS modules. You have done a magnificent job, it seemed an "almost" impossible task.
If I can add my two cents on naming convention: we have a components library that uses the .pcss
extension since we need PostCSS for compilation and we decided to use this naming convention, also we need to import these CSS as module. How would you address this case? We are currently hacking the webpack configuration and patching the test regex of the loaders, but it feels dirty as you may imagine.
I imagine that .module.css
supported postCSS just as global .css currently do.
I know what happens, I think I need a vacation.
When reviewing the PR: https://github.com/zeit/next.js/pull/9686 I thought I "saw" that CSS modules were imported scoped just as global modules are imported.
I sincerely thought I read this code:
index.module.css
.redText {
color: net;
}
import './index.module.css'
export default function Home () {
return (
<div id = "verify-red" className = "redText">
This text should be red.
</div>
)
}
Obviously nothing to do with reality. In reality the real code is this.
import {redText} from './index.module.css'
export default function Home () {
return (
<div id = "verify-red" className = {redText}>
This text should be red.
</div>
)
}
Just CSS Modules as in CRA or Gatsby. Hence the confusion, my confusion.
I apologize to everyone for the mistake.
:-(
When use CSS Modules, the main problem is that:
Use CSS Modules in my project, and some third-party components use CSS Modules too.
But... some third-party components use global CSS.
I think the solution of .module.css
extension is easy to understand and implement.
And the other frameworks (CRA、Gatsby) has reached a consensus.
Until now, I haven't encountered any problems about the extension solution.
So, i hope promote the development of .module.css
extension.
If there are another solution about the CSS Modules problem, it is better.
@bySabi
Although .m.css
is shorter, but i do not think it is better.
Is it min.css
or module.css
?
@xyy94813 or the exact opposite, all css imports are modules, files with .global.scss
will be global ones
@felixmosh
But, It is unfriendly to published components.
I think the convention is a good default, but that some configuration options specific to CSS modules should eventually be made available, for example those which are passed to css-loader
, as some users will want control over how the "scoped" class names are output. An option could be added to supply a regex which determines which CSS files are loaded as "modules"
as some users will want control over how the "scoped" class names are output
Examples of why you'd want that? I can't think of any.
Examples if why you'd want that? I can't think of any.
@timneutkens Well I might prefer to output a naming pattern that conforms basically to the naming pattern of my global classes with only the scoped string at the end.
Beyond this, I was thinking of post-build purging or splitting or loading scenarios where I might want to whitelist a certain namespace, but I haven't worked that out yet
I think it's important to remember that an opinionated framework is meant to make decisions for you in order to eliminate initial setup and enforce uniformity.
Some of those decisions should come with defaults that can be changed through configuration options or extensions, but with everything you make customizable you increase the codebase which creates more code to test, maintain, and document which in turn needs to be maintained as well.
Something like scoped output hash pattern is so granular if you get to a point where you _need_ to overwrite the default it's likely you're at the point where you should just be writing the custom loader you need yourself.
When working with new React devs I've always found it much easier to convey the concept of scoping styles to a component via className. Just give the outer element in your component a unique className, usually the same as your component name, and write your style like this if using SCSS:
.Navbar {
.logo { ... }
.nav-item { ... }
}
I definitely see the benefits of css-modules
, but I'm a bit worried that by no longer supporting the above method Next.js would end up becoming less friendly to new devs. Maybe that's a fine tradeoff to make if you're seeing a lot of people running into css ordering issues, but I wonder if enforcing css-modules
could at least be disabled via next.config.js
. Or maybe we could even have a build-time check that ensures all styles are properly scoped with a unique className, although not sure how doable that is technically.
I'm a bit worried that by no longer supporting the above method Next.js would end up becoming less friendly to new devs
Unless I misunderstood something, I don't think the CSS Modules thing is exclusive, rather the loader would apply CSS Modules' parser only if the file you import has the *.module.css
naming pattern, or *.module.scss
for that matter
@lunelson-bl
It is semi-exclusive as opposed to how CRA or Gatsby enable the parser only when you use the naming pattern Next would disallow non-module importing directly into the component JS.
From the RFC:
/* components/button.js */
import { btnLarge } from './button.module.css'
// import '../styles.css'; // <-- this would be an error
In order to use non-module classes you would need to import the stylesheet into your global stylesheet that gets imported only into _app.js
(if you wanted them split and colocated with your component files). Otherwise they'd need to be placed directly into that global stylesheet.
I am a new user of next.js. After I read this issue and feel a little confused. I think the if the next.js support build-in-css-support , so we don't need @next-css plugin, but it doesn't work and throw error about "ModuleParseError".
If i adding in the @next-css plugin to add CSS imports support, it works, so how build-in-css-support without @next-css , can you provide an example?
thanks @all.
Ben next.js'nin yeni bir kullanıcısıyım. Bu sorunu okuduktan ve biraz kafam karıştı. Ben next.js destek build-css desteği varsa düşünüyorum , bu yüzden @ next-css eklentisine ihtiyacımız yok, ancak işe yaramıyor ve "ModuleParseError" hakkında hata atmıyor.
CSS içe aktarma desteği eklemek için @ next-css eklentisine eklersem, çalışır, bu yüzden @ next-css olmadan nasıl css-build oluşturursanız, bir örnek verebilir misiniz?
teşekkürler @ALL .
add a config to your next.config.js like this:
module.exports = {
experimental: {
css: true
}
}
@erkanunluturk thanks, it's ok, but has a experimental feature warning, does it matter ?
And how is it compatible with antd? I know with-ant-design is ok.
@Timer can you provide an example import ant-design use [RFC] css support? thanks.
I still have problem with Router.push("/") when I am in another link. Anyone has solution for it? Please help me thanks so much
:warning: Please stop using this thread as wishing well and issue tracker. This is an RFC and they are still implementing it.
The last meaningful comment of the team is https://github.com/zeit/next.js/issues/8626#issuecomment-531415968 You can evaluate the current implementation by enabling it via https://github.com/zeit/next.js/issues/8626#issuecomment-568736218
I'm missing the possibility to track the work. How can we help? @Timer is it possible to connect this RFC with milestones, a roadmap? What's done from the points in the RFC?
No idea, how to keep on track :confused:
@StarpTech this RFC is completely implemented; Global CSS and CSS Modules are working per the RFC description.
You can test both with the single experimental flag!
I'll lock this thread to prevent further notification sprawl. If anyone identifies issues, please open a new issue and let us know! We'll post here when the support is officially marked stable (and is on by default).
Landed in 9.2: https://nextjs.org/blog/next-9-2
Most helpful comment
Whatever we do here can we make sure it's still easy to use custom PostCSS plugins like it is now with
@zeit/next-css
? It would be a real shame if we lost support for that — CRA doesn't support this and because of that it's impossible to use tools like Tailwind CSS in any sort of reasonable way.I would be careful about these three features in this regard:
...because this is all done with PostCSS, and it's very important that PostCSS plugin order be under the end-user's control.
I would suggest that if you are going to enable those three plugins by default, that the user should be able to completely override the default PostCSS config by providing their own
postcss.config.js
file. They would have to add those plugins manually if they went this route, but that level of control is necessary in my opinion.TL;DR please be careful not to break the ability for users to have complete control of PostCSS, I literally can't use CRA because of this and I would be very sad if I couldn't use Next anymore.