Using Nextjs with Sass the Angular way!
Tags: nextjs, sass, react, webpack
Published: 2022-02-05 23:47:41

I am someone who has quite good experience in frontend world (11+ years), and I believe frameworks (like Angular) is a great way to keep code style consistency, and quality specially in quite big team.

Frameworks however come with a downside that it can't be easily customized.

I recently started using nextjs to kickstart a new project, and I was shocked by the fact that very simple structure decisions are hard to make! specially when it comes to something as simple as connecting my logic to style files.

Some approach that I am used to very long time ago is separation of concern, and the most simple way in frontend application to apply this, is for example keeping all logic (ts, or js files), and style (css,sass,scss ..etc), and sometimes (html if you dont use SFCs) in the same directory, and also keep them linked together (i.e a single entry point usually imports styles and layout files).

Unfortunately when I started using nextjs I found that keeping my sass files in the same directory as my react component is not as easy as I imagined, but the good news that after few hours of searhcing and trying I found a working solution, and that is why am writing this article to share knowledge and save someone's else time if they happen to be looking for a similar solution.

**Steps:**

- Let's start by creating NEXTJS Hello World project (at the time of writing this article, the latest NextJS version was 12.0.10)

`npx create-next-app@latest --typescript`
- Next we need to install all dependencies
`npm i`
- Next we can run our app
`npm run dev`
So far everything looks cool. However the challenge starts when I want to create a simple page with following structure

```
|-- path
    |-- to
        |-- MyPage
            |-- index.tsx
            |-- style.scss
```
Unfortunately nextjs only allows using modules, which I don't find very convenient!

- Under pages create Mypage folder, and create two new children, `index.tsx`, and `style.scss`

```
// index.tsx
import React from 'react';
import './style.scss';

export default function MyFirstPage() {
  return (
    <div id="my-first-page">
      <h1 className="title-1">Title 1</h1>
      <h1 className="title-2">Title 2</h1>
    </div>
  );
}

```

```
// style.scss
#my-first-page {
  .title-1 {
    color: red;
  }

  .title-2 {
    color: green;
  }
}

```

- import the page we have just created into `app.tsx`

```
import React from 'react';
import Router from 'next/router';
import { AppProps } from 'next/app';
import MyFirstPage from './MyFirstPage';

const App = ({ Component, pageProps }: AppProps) => {
  // return <Component {...pageProps} />;
  return <MyFirstPage />;
};
export default App;

```

- If you try to run the previous code, you will get error from nextjs 

```
Global CSS cannot be imported from files other than your Custom <App>. Please move all global CSS imports to pages/_app.js.
Read more: https://err.sh/next.js/css-global
```

I got stuck at this point for long time, and most solutions on the internet were really inconvenient for me, but luckily I could come up with a working solution!

- install following

```
npm install mini-css-extract-plugin css-loader sass sass-loader
```
- The plugins are optional but just in case you have a previous nextjs config feel free to install them, or just remove it from config file and skip this step

```
npm install next-compose-plugins next-transpile-modules
```

- Open next.config.js and add the following

```
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;
  },
});

```
The above code simply overrides nextjs global css rules, and it also uses MinicssExtractPlugin to write the sass content which was transpiled by sass loader into separate css files

- Run the app once more and here you go ! a nice non scoped styles with only using css specifity
![code preview screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0c89cxhjfp5ycadbc2e6.png)

If you are so lazy like myself, feel free to clone the whole experiment from [here](https://github.com/justgeek/nextjs-global-sass), and play around with it! Happy Hacking ✌️