Neo Genesis
Neo Genesis

Reputation: 980

NextJS component level SASS styling

I am working on a simple NextJS app, but I am having trouble with component level styling using SASS/SCSS. Everything looks fine when the user first lands on the page, but when the user navigates to a page /about for example or from /about to /, the component styles didn't get render, the user needs to refresh the page to see the styles 🤔

⚙️ SETTINGS

./package.json

"dependencies": {
  "@zeit/next-css": "1.0.1",
  "@zeit/next-sass": "1.0.1",
  "autoprefixer": "^9.7.0",
  "next": "^9.3.1",
  "node-sass": "4.13.1",
  "react": "^16.13.0",
  "react-dom": "^16.13.0",
  "webpack": "^4.42.0"
}

./next.config.js

const webpack = require('webpack');
const withSass = require('@zeit/next-sass');
const withCss = require('@zeit/next-css');

module.exports = withCss(
  withSass({
    webpack(config) {
      config.module.rules.push({
        test: /\.(eot|woff?2|ttf|otf|svg|png|jpe?g|gif)$/,
        use: {
          loader: 'url-loader',
          options: {
            limit: 100000,
          },
        },
      });
      return config;
    },
  }),
);

./postcss.config.js

const autoprefixer = require('autoprefixer');

module.exports = {
  plugins: [autoprefixer],
};

📄 PAGES

./pages/index.js

import React from 'react';
import Layout from '../components/Layout';
import Home from '../components/Home';

class Index extends React.PureComponent {
  render() {
    <Layout>
      <Home />
    </Layout>
  }
}

export default Index;

./pages/about.js

import React from 'react';
import Layout from '../components/Layout';
import Company from '../components/Company';

class About extends React.PureComponent {
  render() {
    <Layout>
      <Company />
    </Layout>
  }
}

export default About;

📦 COMPONENTS

./components/Layout.js

import React from 'react';
import AppBar from '../AppBar/AppBar';
import './Layout.scss';

class Layout extends React.PureComponent {
  render() {
    const { children } = this.props;

    return (
      <>
        <AppBar />
        <main className="Layout">
          {children}
        </main>
      </>
    );
  }
}

export default Layout;

./components/Layout.scss

.Layout {
  background: #f00;
}

./components/AppBar.js

import Link from 'next/link';
import './AppBar.scss';

export default () => (
  <header className="AppBar">
    <Link href="/">
      <a>Home</a>
    </Link>
    <Link href="/about">
      <a>About</a>
    </Link>
  </header>
)

./components/AppBar.scss

.AppBar {
  background: #0f0;
}

./components/Home.js

import './Home.scss';

export default () => (
  <div className="Home">Home</div>
)

./components/Home.scss

.Home {
  background: #00f;
}

./components/Company.js

import './Company.scss';

export default () => (
  <div className="Company">Company</div>
)

./components/Company.scss

.Company {
  background: #ff0;
}

Upvotes: 4

Views: 6956

Answers (2)

ProllyGeek
ProllyGeek

Reputation: 15836

For anyone who is looking for a working answer after nextjs 11. You can refer to this repo, or read full details in this article

Here is full next config code that I have used

const withTM = require('next-transpile-modules')([]);

const withPlugins = require('next-compose-plugins');

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = withPlugins([withTM], {
  reactStrictMode: true,
  webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
    // Find and remove NextJS css rules.
    const cssRulesIdx = config.module.rules.findIndex((r) => r.oneOf);
    if (cssRulesIdx === -1) {
      throw new Error('Could not find NextJS CSS rule to overwrite.');
    }
    config.module.rules.splice(cssRulesIdx, 1);

    // Add a simpler rule for global css anywhere.
    config.plugins.push(
      new MiniCssExtractPlugin({
        // Options similar to the same options in webpackOptions.output
        // both options are optional
        filename: 'static/chunks/pages/[contenthash].css',
        chunkFilename: 'static/chunks/pages/[contenthash].css',
      }),
    );

    config.module.rules.push({
      test: /\.(sa|sc|c)ss$/i,
      use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
    });

    config.module.rules.push({
      test: /\.tsx/,
      use: [defaultLoaders.babel],
    });

    return config;
  },
});

Upvotes: 0

Neo Genesis
Neo Genesis

Reputation: 980

This is how I fix my issue. 🎉

With the release of Next.js 9.3, there is no need for @zeit/next-sass & @zeit/next-css. 👏
I also do the following:

  • add styled-jsx-plugin-sass node-sassa as dev-dep
  • create a .babelrc file and add the following:
{
  "presets": [
    [
      "next/babel",
      {
        "styled-jsx": {
          "plugins": ["styled-jsx-plugin-sass"]
        }
      }
    ]
  ]
}

So I have two options to do it:

  • add my sass/scss on the <style jsx>
  • rename my component sass/scss file to <ComponentName>.module.scss

The second option is a pain in the A, you need to import styles from './ComponentName.module.scss and add element class as className={styles.ComponentName} or className={styles['ComponentName-button']}. 🧐

Upvotes: 3

Related Questions