Next.js: Importing CSS files?

Created on 27 Dec 2016  ·  102Comments  ·  Source: vercel/next.js

Sometimes it's nice to break out your CSS into a separate .css file. I've tried to do the following:

pages/
└── index
    ├── index.css
    ├── index.js
    └── component.js

Then in the index.js, I've tried to do:

import css from './index.css'

And in next.config.js:

module.exports = {
  webpack: function (config) {
    config.module.loaders = (config.module.loaders || []).concat({
      test: /\.css$/, loader: 'raw'
    })
    return config
  }
}

But unfortunately, it keeps giving me:

 ERROR  Failed to compile with 1 errors

This dependency was not found in node_modules:

* ./index.css

Seems like it's not resolving to the right place for some reason, the local component.js works though via import component from './component.js', so I'm not sure what's going on here.

Most helpful comment

Here's a simple solution for importing CSS files using babel-plugin-inline-import:

.babelrc

{
  "presets": ["next/babel"],
  "plugins": [
    [
      "inline-import",
      {
        "extensions": [".css"]
      }
    ]
  ]
}

page/component

import "prismjs";

import { PrismCode } from "react-prism";
import prismGlobalStyles from "prismjs/themes/prism.css";

export default () => {
    return (
        <div>
            <style global jsx>
                {prismGlobalStyles}
            </style>
            <PrismCode className="language-javascript">
                {`function(noop) {
                    return noop;
                }`}
            </PrismCode>
            {/* ... */}
        </div>
    );
};

All 102 comments

You mean this doesn't work on server ?
I think we don't have workaround for this yet. cc @arunoda

oh right, i guess there'd need to be a way for the webpack config in next.config.js to work on both sides in order for this to work.

I've got a kind of ballpark similar issue in that I just can't seem to get Next to play nicely with CSS or SASS. I have a components directory with standard React components in that I import into my pages but whenever I attempt to import in SASS (or CSS) files I get a ~"you need to use the appropriate loader for this file" type error message.

Generally in React I import SASS files and have Webpack compile using style, css and sass loaders. I've attempted to add these to the next.config.js file (and NPM installed them) but still get the same error message.

My next.config.js file:

module.exports = {
  webpack: (config, { dev }) => {
    config.module.rules.push({ test: /\.scss$/, loader: ['style-loader', 'css-loader', 'sass-loader'] });
    return config;
  }
}

Sorry if these questions sound dumb or I've missed something obvious in the docs that spell out the answers to them, but if anyone has a working example of importing / compiling SASS (or at the least CSS) into a component or a page with whatever's needed to add to the next.config.js to load / compile them, I'd massively appreciate it. Thanks!

I'm using css-modules-require-hook
to make css work.

@spacedragon Do you have an example how to integrate css-modules-require-hook with Next.js? I'm having problems with getting it working.

I'm still having issues with getting SASS to compile if anyone could shed some light on how to do this or just import a CSS file within Next it would be appreciated (via a code example).

Interesting that the README file has been updated to remove the SVG loader example and changed to say adding loaders for file like SVG, CSS and SASS is discouraged. I'm not sure why inlined CSS is OK but imported CSS isn't but I'm sure there's a good reason why. I'm just currently uncertain about the best strategy to handle none JS defined / inlined CSS and SASS.

@MikeDigitize See comment on #627 and #638 .

It's actually possible and pretty easy to process styles on server side.

directly in node:

require.extensions['.css'] = function(file) {
    console.log(file.id)
    return;
}

via babel register:

// from https://babeljs.io/docs/core-packages/babel-register/
require("babel-register")({
  // Optional ignore regex - if any filenames **do** match this regex then they
  // aren't compiled.
  ignore: /regex/,

  // Ignore can also be specified as a function.
  ignore: function(filename) {
    if (filename === '/path/to/es6-file.js') {
      return false;
    } else {
      return true;
    }
  },

  // Optional only regex - if any filenames **don't** match this regex then they
  // aren't compiled
  only: /my_es6_folder/,

  // Setting this will remove the currently hooked extensions of .es6, `.es`, `.jsx`
  // and .js so you'll have to add them back if you want them to be used again.
  extensions: [".es6", ".es", ".jsx", ".js"]
});

via webpack loaders:

I personally use isomorphic-style-loader as it allows me to inline critical CSS while rendering on server. Hot reloading and other DX related stuff works too. I'm not really fan of CSS in JS as it complicates using 3rd party components and and somehow takes away the C from CSS.

@viktorbezdek Have you successfully used isomorphic-style-loader with next.js?

@noeljackson Not really, but I intend to. Next.js looks promising and could save me lot of time if I make it work. Will look into it in next one or two weeks and submit pull request if I'm successful.

@viktorbezdek I will put a bounty on this one, as it's really crucial to a project I'm working on. I know I'm not inept, but I do not understand how to debug the babel transformations enough to figure this out. I've tried permutation of these ideas and nothing works 100%. I have been able to get raw-loader to pull in a data-encoded stylesheet using babel-plugin-webpack-loaders, but none of the style loaders work. Thanks for chiming in! :) Much appreciated.

Is there a solution to this? I would like to see a solution so I don't have to include css globally.

FWIW, I've just been putting my CSS files in the /static folder. Not great colocation, but not a huge deal either.

There will be a solution. I just didn't manage to finish it. I've locally very first prototype which seems to work, however it needs few hours to be finished. I'm pretty sure I'll be done after weekend. Stay tuned.

@matthewmueller You are using CSS modules?

@viktorbezdek Thanks for working on this! CSS Modules support (or similar) is important for this project IMO. Styled jsx, is ok for simple situations but is hard to read for heavily styled components.

Would a babel plugin like this be an option (server side as well)? https://github.com/gajus/babel-plugin-react-css-modules

I tried to get this to work but but no luck :/

I got CSS modules sort of working with babel-plugin-css-modules-transform. See my hacky example in my fork.

The downside is you have to kill the server & restart it every time you make a change to the CSS.

Some React components expose default styling though a importable static resource. For instance to import the default style of https://github.com/react-component/slider one would use:

import 'rc-slider/assets/index.css';

It sure is possible to copy paste this stylesheet in the static/ directory but it won't stay in sync with the upstream style on a future component update, and it doesn't match what this component documentation recommend.

The problem is that those CSS files introduce global effects. We need to be able to _capture_ the CSS and put it inside the React lifecycle, so that it gets unmounted, server-rendered, etc.

Many libraries do that, but I don't think it's a good pattern.

I'm not familiar with Zeit Next internals, but could some static analysis of the import be used to register/capture the CSS?

We could, but it would be really strange. Similar to something that's outside of render() magically inserting itself inside your component lifecycle.

// Figured I'd share this for anyone else

Well... I just spent a bit too much time trying to hack CSS in here, BUT I landed on a solution that works (for me). Admittedly, it's a hack, but hot reloading works and so does server-side building.

Using (_shudder_) gulp, with this gulpfile (https://gist.github.com/auser/25e88e39d83413773c01f4c000ec2806) all **/*.scss files get concatenated together and shoved into a Styles component which I mount on the page as a "normal" element.

Hope this helps someone else until we can get true postcss support in next.

Thanks for the hack @auser, I've been looking at the webpack config all day with no luck!

Edit:
Btw, you need to add a sass parser to the gulpfile!

Yes and no... I just use the .scss extension as a way to differentiate pure css files from pre-compiled ones. Since postcss (with precss) mimics sass well enough, I don't have one. Feel free to edit for yourself with a sass parser.

Seems like that is currently the best solution, using gulp to compile css file and build it inline or just even in /static if you don't mind not having hot reloading.

I got css import + hot reload working in a clean way. The css is imported as string and the user can inline it in the page just like any other string. Please have a look at this example, help me test and PRs are welcome!

https://github.com/davibe/next.js-css-global-style-test

I believe this example should make it into next.js official examples. Does it? @rauchg @arunoda @nkzawa (sorry if i tagged someone who's not directly involved)

@davibe thanks for your demo and babel-plugin-wrap-in-js

In the example I see use of a CSS file, and a SCSS file. Do you know if this would work with postcss & cssnext?

@khrome83 I don't see why not, I think its just a matter of adjusting .babelrc and next.config.js

@davibe I found that I was unable to deploy my app based on your configuration. The build was unable to read next/babel in the .babelrc file. I submitted an issue, but I'm really hopeful that a solution emerges from all this. Missing the ease of import file.css from create-react-app, but I know there must be a solution coming :)

The solution I want is likely along these lines:

https://github.com/zeit/styled-jsx/pull/100#issuecomment-277133969

We might support importing .css (by means of simply transpiling it into a module that exports a string) (and likewise we might support .svg by transpiling it into a pure react component)

and likewise we might support .svg by transpiling it into a pure react component

This is a pretty simple trick. I'll create a basic example showing how I handled this in another project =)

EDIT: see https://github.com/zeit/next.js/pull/982

Based on @davibe sample I've created https://github.com/moxystudio/next.js-style-loader that will hopefully ease out adding css files into next.js projects. It's similar to webpack's style-loader in that it will add/remove stylesheets as the user navigates. It also supports SSR.

It does work well with css-loader (with and without css modules), postcss-loader, sass-loader and possibly others. Note that when css-loader is used, its url option must be set to false beucase next.js images, fonts, etc must live /static. You will find all this information in the README.

Enjoy and please give me feedback!

Thanks for the repo! It worked for importing the css files. I am trying blueprintjs and it seems like the css is loading correctly! However the @font-face rule that the css is including doesn't seems to work. :

--------------------edit----------------------

It is actually working, my bad!
However the icons are not being loaded because nextjs routing by default doesn't allow serving static content outside /static/ and relative path actually cause it to load with path that is not permitted.

@pencilcheck yes you must use paths pointing to /static, perhaps I will make that more clear in the README.

Is there any workaround regarding relative path included in the css files such as fonts atm? Or I simply have to copy the whole font files and css into static folder in order for it to work?

@pencilcheck the CSS files can stay outside of static. Your images and fonts must be inside static and you reference them with /static/file.

I get it. However I'm using blueprint, which is an npm package, I would like to be able to not have to modify any files inside node_modules.

@pencilcheck That's not possible unfortunately. next.js is very strict in how it handles images and other assets. Lets not pollute this conversation and please create an issue in next-style-loader repo if you may.

@tgoldenberg can you describe the problem better or tell me how to reproduce ? please refer to my repository. It's easyer for me to track issues there.

@davibe, it ended up being a problem using yarn over npm install. Yarn was throwing some inexplicable errors, but once I removed it the example worked fine in production.

I just spent 4 hours trying to set this up and thought it might be of interest for anybody looking to save some time. It applies styles automatically on change (just like in the current example), runs the CSS through PostCSS and gives you local module names from css-loader. The lack of the latter were major deal breakers for me in the current state of the “global css”/“css imports” examples.

component.js

import React from 'react';
import stylesheet from './styles.css';

const [css, className] = stylesheet;
const Component = () => (
    <p className={className.paragraph}>
        <Head><style dangerouslySetInnerHTML={{__html: css}} /></Head>
        bazinga
    </p>
);

.babelrc

{
    "presets": [
        "next/babel"
    ],
    "plugins": [
        ["wrap-in-js", {
            "extensions": ["css$"]
        }]
    ]
}

next.config.js
Notice the amazing hack with exports-loader. There's got to be a better way, surely???

module.exports = {
    webpack: (config) => {
        config.module.rules.push(
            {
                test: /\.css$/,
                use: [
                    {
                        loader: 'emit-file-loader',
                        options: {
                            name: 'dist/[path][name].[ext]'
                        }
                    },
                    {
                        loader: 'raw-loader'
                    },
                    {
                        loader: 'val-loader'
                    },
                    {
                        loader: 'exports-loader',
                        options: {
                            0: 'exports[0][1]',
                            1: 'exports.locals'
                        }
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true,
                            minimize: true
                        }
                    },
                    {
                        loader: 'postcss-loader'
                    }
                ]
            }
        );

        return config;
    }
};

I came up with a solution myself, which is very similar to what @satazor posted higher up in the thread: https://github.com/jozanza/next-css-json-loader.

Just add a few lines to your next.config.js:

module.exports = {
  webpack: config => {
    config.module.rules.push({
      test: /\.css$/,
      loader: 'emit-file-loader',
      options: {
        name: 'dist/[path][name].[ext]',
      }
    }, {
      test: /\.css$/,
      loader: 'babel-loader!next-css-json-loader',
    });
    return config;
  },
};

Styles are imported as js objects so it's very easy to use with glamor and similar solutions:

// .css files now conveniently expose all styles as js objects
import styles from 'some-package/styles.css';
import { css } from 'glamor';
// ...
<div {...css(styles)}>
  this is a nice box. 
</div>

Cheers! 🍻 :)

Is there a way to make this work for importing markdown files as strings? I am currently using raw-loader, but as it is a webpack plugin it doesn't work on the server.

@kristojorg

I just wrote a babel plugin for markdown import. On my mobile right now, but if you view my GitHub you will see it.

@khrome83 that sounds awesome. Looking forward to trying it out

Thank you @khrome83! I'll give it a shot

I had to do this really quickly, so I did not update the readme. But you just include it as a babel plugin, and then use

import File from 'File.md';

I got it to work, thank you !! Very helpful

This is offtopic but since the issue is closed i feel free to elaborate :)

For markdown there are 2 things you can do.

(1)
One is to include the markdown file as a string and then translate it to html/react at runtime. You can do this using the generic wrap-in-js babel loader registering it for the markdown file extension just like it is used in the next.js example examples/with-globa-stylesheet/.babelrc for css.

(2)
Another thing you can do is to translate markdown at transpilation time using markdown-in-js
This can be more interesting in some scenario because the markdown document is already pre-rendered therefore at runtime it is faster (it just executes js). Also, the library allows you to injiect custom React components. Unfortunately in this case you have to write markdown inline in your source.js like this.

If you pick (2) also know that there is an atom plugin providing syntax hilight for markdown-in-js syntax and it's called language-markdown-in-js

@davibe thank you for the tip! I would rather the markdown parsed as build time, but the only problem I have with markdown-in-js is escaping the backticks while composing :(. Maybe I should try importing as string using babel and then feeding that to markdown-in-js?

@kristojorg

There are out her markdown renders. The reason I wrote my plugin was the same thing. I pull markdown as a string, then I run that through react-markdown. This works increasingly well because I can pass in react components to represent markdown render pieces, like List component to handle all lists. Works good with Styled JSX.

Is there any update on this? Through the comments above, I see there is still no perfect solutions currently.
@arunoda
Is it possible to have a example using isomorphic-style-loader - https://www.npmjs.com/package/isomorphic-style-loader?

That would be perfect!

Anyone have a solution for when importing a react component from npm package that in turn imports .css or .scss file? so basically transpiling files that are imported from node_modules

I've been using Create-React-App (CRA ) for a few weeks but today I came across Next.js and I got really excited because CRA doesn't currently support server-side rendering (SSR) which is a real shame.
I'd love to switch to Next.js for its SSR support out of the box but not being able to import .scss files is holding me back.
Has anyone found a way to add a SASS loader to the Webpack config?

Agreed with cr101, not having a support for CSS/SASS means I have to throw out css frameworks like foundation which is not an option for me.

I was using the repo provided by @davibe but he is no longer maintaining it, and the latest build breaks with the solution he had.

Would love if someone has found a fix for this. Updating to 2.4.1 was absolutely necessary due to the security bug, so I don't have the option of going back down.

@Yuripetusko @cr101 @tgoldenberg Absolutely agree. I think honestly that next is really awesome to be able to work out of the box. Everything is okay even hard-coded structure (like mandatory /pages directory, /static etc.). But totally unsupported scss modules breaks entire thing for us. We have a very complex scss structure with automatic breakpoints and aspect ratio recalculation of everything. We worked so hard to make our structure so perfect. Next is great be we can't just throw out all the scss stuff that we have at the moment of being. Unsupported scss is the only but the most crucial thing that stops us from migration to this beautiful tool.

Actually, #1615 - there is some action going on. @timmywil is trying to setupisomorphic-style-loader to work with next. Everyone who interested is welcome to join and participate.

I tried every solution, which this thread points to, however could make any of them work. So I decided to try to make my own attempt and I have documented it here

@almeynman thank you! definitely going to see at your approach!
By the way, I've managed to make scss modules work by following this example. All I've done is added sass-loader and enabled sourcemaps.

I managed to make next.js suport CSS (even SCSS) in this way.

First, in next.config.js , tweak webpack config by adding given loaders and a DefintPlugin instance.

const webpack = require('webpack');

module.exports = {
  webpack: (config, {dev}) => {
    config.module.rules.push({
      test: /\.scss$/,
      use: [
        'style-loader',
        'css-loader',
        'sass-loader'
      ],
      exclude: /node_modules/,
    });

    config.plugins.push(
      new webpack.DefinePlugin({
        "process.env": {
          // flag to indicate this is for browser-side
          BROWSER: JSON.stringify(true),
       }
      })
    );

    return config;
  }
};

Then, in component code, require style file with condition. It makes sure only browser side bundling will have have style module.

if (process.env.BROWSER) {
  require("./style.scss");
}

If you don't mind the if (process.env.BROWSER), it works perfectly.

A good approach is here

@almeynman IMO that's not a very good approach as you are loading the CSS code for the entire website on every page instead of only loading the CSS styles relevant to that page.
Only importing the .scss files you need instead of the CSS for the entire website would greatly reduce the size of the pages by only loading the CSS code that you need.

@cr101 Hi, I did not know that. I have not really used that setup, just posted it for reference for others (I thought it is good...). I still use my approach described in the blog post. If you could give me some feedback on that setup would be great

More examples and discussions if anyone is interested:

https://github.com/iaincollins/nextjs-starter
https://github.com/zeit/next.js/issues/2534
https://github.com/zeit/next.js/tree/v3-beta/examples/with-global-stylesheet

Based on the above and this thread, I was able to use PostCSS to transform:

  • Global SCSS file (modules are the same, you just need an entry point in order to pre-compile all your CSS for production).
  • Separate 3rd party CSS with custom fonts referenced through relative URL (solved through inlining magic).

into a single CSS file at /static/styles/app.css to serve in production, with hot reloading still working. Notice the usage of styled-jsx but it can be done without, by using <style dangerouslySetInnerHTML={} />

postcss.config.js

module.exports = {
  plugins: [
    require("postcss-easy-import")({ prefix: "_" }), // keep this first
    require("postcss-url")({ url: "inline" })
  ]
};

next.config.js

Loaders to transform .scss in dev mode for hot-reloading and extract to single .css file in prod. This gives me build/app.css so on Production build, I added cp build/app.css static/styles/app.css after next build to have it available in static export and embed in custom header as shown below.

const ExtractTextPlugin = require('extract-text-webpack-plugin');

export default {
  webpack: (config, { dev }) => ({
    ...config,
    module: {
      ...config.module,
      rules: [
        ...config.module.rules,
        {
          test: /\.(css|scss)/,
          loader: 'emit-file-loader',
          options: {
            name: 'dist/[path][name].[ext]'
          }
        },
        ...(dev
          ? [
              {
                test: /\.css$/,
                use: ['raw-loader', 'postcss-loader']
              },
              {
                test: /\.s(a|c)ss$/,
                use: [
                  'raw-loader',
                  {
                    loader: 'postcss-loader',
                    options: {
                      sourceMap: true
                    }
                  },
                  {
                    loader: 'sass-loader',
                    options: {
                      sourceMap: true
                    }                    
                  }
                ]
              }
            ]
          : [
              {
                test: /\.(css|scss)/,
                use: ExtractTextPlugin.extract({
                  use: [
                    {
                      loader: 'css-loader',
                      options: {
                        importLoaders: 2,
                        modules: false,
                        url: true,
                        minimize: true,
                        localIdentName: '[hash:base64:5]'
                      }
                    },
                    {
                      loader: 'postcss-loader'
                    },
                    {
                      loader: 'sass-loader'
                    }
                  ]
                })
              }
            ]),
        {
          test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
          loader: 'url-loader?limit=100000&&name=[name].[ext]?[hash:8]'
        }
      ]
    },
    plugins: [
      ...config.plugins,
      ...(dev ? [] : [new ExtractTextPlugin('app.css')])
    ]
  }),
};

custom header

const inlineCSS =
  process.env.NODE_ENV !== ENV_PRODUCTION && require('styles/index.scss');
...
      <Head>
        {inlineCSS && <style jsx global> {__html: inlineCSS} </style>}
          {process.env.NODE_ENV === ENV_PRODUCTION &&
            <link
              rel="stylesheet"
              type="text/css"
              href={`/static/styles/app.css?${this.props
                .__NEXT_DATA__.buildId}`}
            />}
      </Head>

Hope this helps. Let me know if anyone needs more clarification. Looking forward to more solutions as well. Hopefully someone can come up with a good plugin eventually as it's still rather tricky and heavy-handed integration.

Instead of extracting all your .scss files a single CSS file it would be much better to compile each imported .scss file into into its own CSS file. That way you will only load the CSS styles you need on each page.
I'm not sure how you'd do that though.

Please checkout my pull request, I think I have a good solution:
https://github.com/zeit/next.js/pull/2638

@cr101 that's true actually. We are importing our own internal UI library into different projects hence there is always a huge chunk of a file to load (not ideal I know, still working on modularising that beast). It would be the the next stepfrom compile and serve 1 file to X number of files at X locations. It gets more complicated when you factor in tradeoffs from breaking into smaller CSS chunks vs. external cachable AND performant CDN-hosted versions so I think it'll be a fun but involving project on its own. EDIT - definitely way out of scope for what Next is meant to handle, best we should aim for is a Next plugin or pattern.

Not sure if this has any performance issue, but this is a pretty simple solution if you are using styled-components or similar, just make a CSS wrapper:

import styled from 'styled-components';

const Collapse = props => (
  <__Collapse>
    { props.children }
  </__Collapse>
);

export default Collapse;

/**
 * CSS
 */
const __Collapse = styled.div`
  .rc-collapse {
    background-color: #f7f7f7;
    border-radius: 3px;
    border: 1px solid #d9d9d9;
  }
  ...
`;
import RcCollapse from 'rc-collapse';
import Collapse from '~/components/rc/Collapse';

const HelpPage = () => (
  <Collapse>
    <RcCollapse>
      <RcCollapse.Panel header="Title">Content</RcCollapse.Panel>
    </RcCollapse>
  </Collapse>
);

The thing that I like about this approach is that I can customise from the source CSS (which I need to do most of the times anyway), without having to write overrides on top of the original rules if I have imported the .css file from node_modules.

Here's a simple solution for importing CSS files using babel-plugin-inline-import:

.babelrc

{
  "presets": ["next/babel"],
  "plugins": [
    [
      "inline-import",
      {
        "extensions": [".css"]
      }
    ]
  ]
}

page/component

import "prismjs";

import { PrismCode } from "react-prism";
import prismGlobalStyles from "prismjs/themes/prism.css";

export default () => {
    return (
        <div>
            <style global jsx>
                {prismGlobalStyles}
            </style>
            <PrismCode className="language-javascript">
                {`function(noop) {
                    return noop;
                }`}
            </PrismCode>
            {/* ... */}
        </div>
    );
};

@stovmascript this is a beautiful solution but I still get errors (I'm importing the .css build from https://github.com/Hacker0x01/react-datepicker). Are you sure you don't have anything else in play here? From the errors it looks like it needs another level of CSS loading.

@hilarykitz , @stovmascript solution works for me, can you send us the error you are getting?

@stovmascript - how are you getting rid of the babel cache.

  1. Make CSS file
  2. Import File
  3. Apply
  4. Change CSS file - add new selector and style
  5. Witness that Babel Cache keeps old version

@khrome83 you'll need to clear the node_modules/.cache

I found a better solution using babel-inline-loader plugin that clears the cache and works with the above. The issue with that method is that you can only apply global styles. When using in a non <style jsx global> tag, it will not add the data-jsx correctly defeating the purpose.

I found a better solution using babel-inline-loader plugin that clears the cache and works with the above. The issue with that method is that you can only apply global styles. When using in a non