Using Nextjs with Sass the Angular way!

justgeek

Mohammed Taher

Posted on February 5, 2022

Using Nextjs with Sass the Angular way!

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
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode
// style.scss
#my-first-page {
  .title-1 {
    color: red;
  }

  .title-2 {
    color: green;
  }
}

Enter fullscreen mode Exit fullscreen mode
  • 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;

Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode
  • 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;
  },
});

Enter fullscreen mode Exit fullscreen mode

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

If you are so lazy like myself, feel free to clone the whole experiment from here, and play around with it! Happy Hacking ✌️

💖 💪 🙅 🚩
justgeek
Mohammed Taher

Posted on February 5, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related