Next.js: Working with external css and scss files

Created on 19 Oct 2017  ·  38Comments  ·  Source: vercel/next.js

It is a redundant issue I know, but I opened this issue intentionally. It's three days I'm setting up a next.js boilerplate(with redux, redux-saga, ...) and it's two days that I'm stucked in setting up loading external css and scss files. I've checked with-global-stylesheet and with-scoped-stylesheet-and-postcss examples, but each of them has major problems mentioned in prior issues. I've seen too many open and closed issues that are solving this problem with hacks... I think it's a good idea that instead of leaving the problem to find the best solution, solve it with current available solutions till find a better one. Because many have this problem now and want to see it solved now!

Most helpful comment

I'd also like to point out that I personally don't like the tone people are having in this issue.
I totally understand you want to import css. And we're very aware of this request. That's why I've spent the past week working on the best possible solution 👍
More on this soon. Till then, please be kind and happy holidays 🎅😄

All 38 comments

I agree, only styled-jsx has clean (incl. hot reloading) support and that's what keeping me from using Next.js for anything but when I need some rapid prototyping.

I think the solution to CSS' scoping issues of CSS Modules is a much cleaner one, plus with CSS Modules it's still possible to pass classes to child components (try putting a non-global class on an SVG imported with babel-plugin-inline-react-svg with styled-jsx).

That and I prefer to have standardized .css files to prevent framework lock-in as much as possible and an external CSS files in production for caching (and to make MQ polyfills like Respond.js work if you are unlucky enough to still have to support IE8).

Massive +1
It's a huge frustration that such a simple thing as external css/scss is almost impossible to achieve with next.js, which renders it useless for 90% of my applications.

I'm working with bootstrap, and I need a configuration where there will be one global bootstrap css import, with addition of external scoped css.

While we managed to get working external stylus with styled jsx 1 ( webpack for handling compilation ), having hard time figuring it out on styled jsx 2 since breaking change of handling separate css files introduced.
Current approach:

import ComponentStyles from './footer.styl';
...
      <style jsx>
        {ComponentStyles}
      </style>

Would be great to see https://github.com/zeit/next.js/tree/master/examples/with-styled-jsx-scss working with external scss files.

We went through the same ordeal when setting up the environment.
Eventually we settled with a global stylesheet with scss+post css with lost-grid.
Hot reloading works, so while it's not an ideal solution (due to the global stylesheet being loaded all at once), it's an ok compromise.

Dependencies

"autoprefixer": "^7.1.6",
"babel-plugin-module-resolver": "^2.7.1",
"babel-plugin-wrap-in-js": "^1.1.1",
"node-sass": "^4.5.3",
"sass-loader": "^6.0.6",
"pixrem": "^4.0.1",
"postcss-easy-import": "^3.0.0",
"postcss-loader": "^2.0.8"

In package.json

  ...
  "postcss": {
    "plugins": {
      "lost": {},
      "postcss-easy-import": {
        "prefix": "_"
      },
      "autoprefixer": {},
      "pixrem": {}
    }
  }
  ...

In next.config.js

webpack: (config, { dev }) => {
    config.module.rules.push(
      {
        test: /\.(css|scss)/,
        loader: 'emit-file-loader',
        options: {
          name: 'dist/[path][name].[ext]'
        }
      }
    ,
      {
        test: /\.css$/,
        use: ['babel-loader', 'raw-loader', 'postcss-loader']
      }
    ,
      {
        test: /\.s(a|c)ss$/,
        use: ['babel-loader', 'raw-loader', 'postcss-loader',
          { loader: 'sass-loader',
            options: {
              includePaths: ['styles', 'node_modules']
                .map((d) => path.join(__dirname, d))
                .map((g) => glob.sync(g))
                .reduce((a, c) => a.concat(c), [])
            }
          }
        ]
      }
    )
    return config
  }

In pages/_document.js

...

import stylesheet from 'styles/main.scss'

   ...
        <Head>
          <style dangerouslySetInnerHTML={{ __html: stylesheet }} />
        </Head>
   ...

And you can then manage your styles starting from /styles/main.scss
Hope it helps

My problem with each of these two style examples ( with-global-stylesheet and with-scoped-stylesheets-and-postcss ) is that none of them are simple to integrate with Jest and Snapshot testing with the CSS in the snapshot. There have been people who successfully get Jest to work with Webpack, but that's by specifically skipping the CSS.

Running a babel-jest preprocessor file as described in this SO answer seems like such a bad hack.

It seems to get external CSS as with-global-stylesheet you must use Webpack, but to use Jest you can't rely on Webpack, only Babel.

Does anyone have ideas in this space?

I'm facing a similar problem. I'm new to nextjs and I can't make the example "with-external-scoped-css" work properly. Sometimes, my css is loaded and sometimes not. I don't know if it's the same issue you're talking about.

https://github.com/zeit/next.js/issues/3276

Resolved external styles issues with this loader https://github.com/coox/styled-jsx-css-loader

@ilionic I've checked your solution. It's great! Thank you :)

@arefaslani I don't think this issue is closed.

As of HTTP v1 it's still a horrible performance tax for tons of CSS to load, it increases the time to first draw dramatically.

Proper external style support would allow for importing CSS and it resulting in a not an inline