Skip to content
This repository has been archived by the owner on Jul 6, 2021. It is now read-only.

next-css SSR not working with CSS files from node_modules #70

Closed
thibautRe opened this issue Feb 21, 2018 · 14 comments
Closed

next-css SSR not working with CSS files from node_modules #70

thibautRe opened this issue Feb 21, 2018 · 14 comments

Comments

@thibautRe
Copy link

thibautRe commented Feb 21, 2018

Hi guys, great work with Next!

I encountered an issue which involves next-css, node_modules and Server-Side Rendering.

Basically, when a NPM package requires a CSS file, SSR breaks. This is an issue when trying to use a component library that bundles CSS files in itself (in my case, fyndiq-ui).

The bug happens with or without CSSModules, and with or without PostCSS-loader. It also happens in production build

Note that the bug doesn't happen when importing local CSS files (thanks to #50 I presume).

I made a small repo that demonstrates the issue https://github.com/thibautRe/next-css-ssr-bug. You can run the README instructions to reproduce the bug.

I'm willing to submit a PR to fix this behavior. I checked the next-css code but didn't see anything glaringly problematic.

@grvcoelho
Copy link

Hi @thibautRe thanks for opening this issue, I'm having the same problems :)

I did some deep investigation on how the next.js webpack compilation works and on what the next-css plugin does.

What is my scenario?

  1. On my project I was trying to import a package that has some js and imports some css.
  2. When I imported the project as import theme from 'former-kit-skin-pagarme' it wouldn't work ❌. The CSS would be compiled and extracted to static/styles.css, but I would have an error when accessing an URL.
  3. When I imported the project with the absolute path import theme from './node_modules/former-kit-skin-pagarme/dist' it would work ✅.

What I found out

I created an isolated environment, skimmed through the code and recreated next-css step-by-step to determine if some configuration, loader or plugin could be the problem. And, as it turns out, there's nothing wrong with next-css or its configuration.

So my next suspect was next.js itself. With the behavior I noticed, I knew that there would probably be some configuration related to the node_modules folder. next.js have two distinct (but similar) webpack configurations: one that runs to compile for the server and on for the client part of your application.

On the server, configuration, next.js basically marks everything from node_modules (except webpack) as a webpack externals. 👉 Link to the source code

Simply put, every dependency that appears on webpack externals do not get bundled, but instead webpack thinks you will want to require the package by yourself (which makes a lot of sense on a server context) and it puts this on your build: require('former-kit-skin-pagarme). Now, here's the problem: the server part of your application is going to try to require your package that requires some CSS. The result: your application will face a runtime error because your server will try to require some plain CSS file.

Workaround

Once I found out what was happening, it was easier to come with a workaround to fix my problem. As any workaround, this is not ideal but can solve some problems and buy time until the problem is addressed on the core of next.js.

Basically, I recreated the externals configuration used by next.js to blacklist my module from webpack externals. Here's what my next.config.js look like:

const resolve = require('resolve')
const withCSS = require('@zeit/next-css')

module.exports = withCSS({
  cssModules: true,

  webpack (config, options) {
    const { dir, isServer } = options

    config.externals = []

    if (isServer) {
      config.externals.push((context, request, callback) => {
        resolve(request, { basedir: dir, preserveSymlinks: true }, (err, res) => {
          if (err) {
            return callback()
          }

          // Next.js by default adds every module from node_modules to
          // externals on the server build. This brings some undesirable
          // behaviors because we can't use modules that require CSS files like
          // `former-kit-skin-pagarme`.
          //
          // The lines below blacklist webpack itself (that cannot be put on
          // externals) and `former-kit-skin-pagarme`.
          if (
            res.match(/node_modules[/\\].*\.js/)
            && !res.match(/node_modules[/\\]webpack/)
            && !res.match(/node_modules[/\\]former-kit-skin-pagarme/)
          ) {
            return callback(null, `commonjs ${request}`)
          }

          callback()
        })
      })
    }

    return config
  },
})

Important: in this case, the project I want to use is called former-kit-skin-pagarme. You should change this on your next.config.js to use your own projects

Definitive solution

The definitive solution must come from next.js core team, as this problem mainly happens because the way the webpack externals configuration are used on the server side.

The ideal solution would be to add externals conditionally: only add if the module itself does not require any CSS. However, I don't know how one would do that with webpack.

@thibautRe
Copy link
Author

Very interesting investigation, thank you for sharing it here. Your solution works really nicely for my use-case, and fixed my issue 🎉.

As this issue doesn't seem to be a problem with next-css, I will close this issue here.

@markolofsen
Copy link

example does not work

@Rafaell416
Copy link

Server is stuck compiling
screen shot 2018-09-26 at 2 45 07 pm

@adamthewan
Copy link

Same here, compiles then crashes for me

@hansiemithun
Copy link

Same issue, how to configure sass with react flext box grid?

@lukeupup
Copy link

Get this problem fixed by adding this plugin: https://www.npmjs.com/package/next-plugin-transpile-modules

Just make webpack to transpile the module containing css imports.

Something like

const withTM = require('next-plugin-transpile-modules');
module.exports = withTM({
  transpileModules: ['the-package-that-imports-css']
}, ...)

@Vrq
Copy link

Vrq commented Mar 20, 2019

If you are coming from Google search, especially if you see the:

Module build failed: ModuleParseError: Module parse failed: Unexpected character '' (1:0) You may need an appropriate loader to handle this file type.

this might be the solution you are looking for.

3 simple steps:

  1. Install next-css plugin:
npm install --save @zeit/next-css
  1. Create in your root directory next.config.js with the following content:
// next.config.js 
const withCSS = require('@zeit/next-css')

module.exports = withCSS({
  cssLoaderOptions: {
    url: false
  }
})
  1. Now you should be able to import styleshets from node_modules like this:
import 'bootstrap-css-only/css/bootstrap.min.css';

Note: Using Next v 8+

Background:
I spent a few hours trying to simply import a CSS installed as a node_module and the various solutions are mostly hacky workarounds, but as shown above, there is a simple solution.
It was provided by one of the core team members: https://spectrum.chat/next-js/general/ignoring-folders-files-specifically-fonts~4f68cfd5-d576-46b8-adc8-86e9d7ea0b1f

@Tanapruk
Copy link

T

If you are coming from Google search, especially if you see the:

Module build failed: ModuleParseError: Module parse failed: Unexpected character '' (1:0) You may need an appropriate loader to handle this file type.

this might be the solution you are looking for.

3 simple steps:

  1. Install next-css plugin:
npm install --save @zeit/next-css
  1. Create in your root directory next.config.js with the following content:
// next.config.js 
const withCSS = require('@zeit/next-css')

module.exports = withCSS({
  cssLoaderOptions: {
    url: false
  }
})
  1. Now you should be able to import styleshets from node_modules like this:
import 'bootstrap-css-only/css/bootstrap.min.css';

Note: Using Next v 8+

Background:
I spent a few hours trying to simply import a CSS installed as a node_module and the various solutions are mostly hacky workarounds, but as shown above, there is a simple solution.
It was provided by one of the core team members: https://spectrum.chat/next-js/general/ignoring-folders-files-specifically-fonts~4f68cfd5-d576-46b8-adc8-86e9d7ea0b1f

This solves the problem. Thanks.

@scottydev
Copy link

@Vrq your posted solution does not work if cssModules: true

@SaroarShahan
Copy link

provided by one of the core team mem

This solves the problem! Thanks dude :)

@axeen
Copy link

axeen commented Nov 21, 2019

Get this problem fixed by adding this plugin: https://www.npmjs.com/package/next-plugin-transpile-modules

Just make webpack to transpile the module containing css imports.

Something like

const withTM = require('next-plugin-transpile-modules');
module.exports = withTM({
  transpileModules: ['the-package-that-imports-css']
}, ...)

This solved it for me!! Thank you.
Although the module has changed name to next-transpile-modules

@ghost
Copy link

ghost commented Dec 18, 2019

I've been using RMWC, which requires users to load additional CSS. Importing the CSS files directly in the JSX files (eg. import "@material/textfield/dist/mdc.textfield.css") was working for me in development, but for some reason they weren't getting included in production.

What worked for me (for reasons I don't really understand) was creating a local CSS file, importing it from the JSX file, and within it importing the RMWC CSS, like so:

/* pages/mypage.css */
@import "~@material/textfield/dist/mdc.textfield.css";
@import "~@material/floating-label/dist/mdc.floating-label.css";
@import "~@material/notched-outline/dist/mdc.notched-outline.css";
@import "~@material/line-ripple/dist/mdc.line-ripple.css";
// pages/mypage.jsx
import "./mypage.css";

const MyPage = () => { //....

Possibly related: #498. Note that I wasn't getting errors in production that I could see -- the stylesheets were seemingly just silently dropped in prod.

@roganoalien
Copy link

I've been using RMWC, which requires users to load additional CSS. Importing the CSS files directly in the JSX files (eg. import "@material/textfield/dist/mdc.textfield.css") was working for me in development, but for some reason they weren't getting included in production.

What worked for me (for reasons I don't really understand) was creating a local CSS file, importing it from the JSX file, and within it importing the RMWC CSS, like so:

/* pages/mypage.css */
@import "~@material/textfield/dist/mdc.textfield.css";
@import "~@material/floating-label/dist/mdc.floating-label.css";
@import "~@material/notched-outline/dist/mdc.notched-outline.css";
@import "~@material/line-ripple/dist/mdc.line-ripple.css";
// pages/mypage.jsx
import "./mypage.css";

const MyPage = () => { //....

Possibly related: #498. Note that I wasn't getting errors in production that I could see -- the stylesheets were seemingly just silently dropped in prod.

This was the only thing that worked for me. In my case Im working with SaSS and added the default config from next-sass but was having a lot of problems importing some package styles. Importing the css inside my sass file is what worked for me

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests