32

I want to use Next.js with Sass and CSS modules but also want to use Ant Design and wanted to use the Less styles for smaller building size.

I'm able to enable either CSS modules or Less loader but not both at the same time. The examples from Next.js were not helping me complete that problem.

Penny Liu
  • 15,447
  • 5
  • 79
  • 98
JustTB
  • 819
  • 1
  • 8
  • 11

7 Answers7

39

Edit: This answer is definitely outdated for current versions of next.js, check the other answers below.

After multiple hours of research I found now finally the right solution and wanted to share it:

.babelrc (no magic here)

{
  "presets": ["next/babel"],
  "plugins": [
    [
      "import",
      {
        "libraryName": "antd",
        "style": true
      }
    ]
  ]
}

next.config.js:

/* eslint-disable */
const withLess = require('@zeit/next-less');
const withSass = require('@zeit/next-sass');
const lessToJS = require('less-vars-to-js');
const fs = require('fs');
const path = require('path');

// Where your antd-custom.less file lives
const themeVariables = lessToJS(
  fs.readFileSync(path.resolve(__dirname, './assets/antd-custom.less'), 'utf8')
);

module.exports = withSass({
  cssModules: true,
  ...withLess({
    lessLoaderOptions: {
      javascriptEnabled: true,
      modifyVars: themeVariables, // make your antd custom effective
      importLoaders: 0
    },
    cssLoaderOptions: {
      importLoaders: 3,
      localIdentName: '[local]___[hash:base64:5]'
    },
    webpack: (config, { isServer }) => {
      //Make Ant styles work with less
      if (isServer) {
        const antStyles = /antd\/.*?\/style.*?/;
        const origExternals = [...config.externals];
        config.externals = [
          (context, request, callback) => {
            if (request.match(antStyles)) return callback();
            if (typeof origExternals[0] === 'function') {
              origExternals[0](context, request, callback);
            } else {
              callback();
            }
          },
          ...(typeof origExternals[0] === 'function' ? [] : origExternals)
        ];

        config.module.rules.unshift({
          test: antStyles,
          use: 'null-loader'
        });
      }
      return config;
    }
  })
});

The final hint how to write the withSass withLess use and to put the cssModules: true in the outer object came from this comment here.

While I was already trying different combinations derived from the examples before: next+ant+less next+sass

For completion here the dependencies in my package.json:

...
"dependencies": {
    "@zeit/next-less": "^1.0.1",
    "@zeit/next-sass": "^1.0.1",
    "antd": "^4.1.3",
    "babel-plugin-import": "^1.13.0",
    "less": "^3.11.1",
    "less-vars-to-js": "^1.3.0",
    "next": "^9.3.4",
    "node-sass": "^4.13.1",
    "null-loader": "^3.0.0",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "sass": "^1.26.3"
  }
...

I hope this helps other people to find this solution faster. :)

JustTB
  • 819
  • 1
  • 8
  • 11
  • 2
    Any chance we can use 'sass' instead of '@zeit/next-sass'. I want to use the built-in sass support that comes with NextJs 9.3 and also use antd less. The moment we use external css loaders in webpack config, the built-in sass support gets disabled. – xaxist Apr 30 '20 at 06:12
  • I tried but did not find a solution for that. If you find one please tell me/post it. – JustTB May 01 '20 at 10:53
  • Finally a solution to what I needed - works for me too, thank you so so much! – mllm Jun 02 '20 at 21:17
  • where you able to add global styles to the /styles directory which did not get compiled as css modules? – Holly Jun 24 '20 at 16:36
  • 1
    @Holly yes, just import the scss in the _app.js and write global styles in there: `:global{ ... }` – JustTB Jun 27 '20 at 08:52
  • 1
    @JustTB, I've tried your configuration with my project, but **CSS Module** feature is not working. [Next.js supports CSS Modules using the [name].module.css file naming convention](https://nextjs.org/docs/basic-features/built-in-css-support). Do you have any recommendation for this case ? Can you review & correct configuration for me ? [Here is my test project](https://github.com/thobn24h/nextjs-with-ant.git). Thank you! – Tho Bui Ngoc Jul 04 '20 at 07:05
  • @ThoBuiNgoc sorry I just read it now. I tried a lot and can't remember that this documentation existed or was working at that point. I'm pretty sure it will improve with the next version. And I'm sorry I can't help you really since I also lack the understanding of writing a nice next.config.js. Will add to this post as soon as I see better things coming up. And I have no time at the moment to debug your config. I hope you find/found a solution. :) – JustTB Jul 21 '20 at 17:22
  • Thanks for your solution, what about hot reloading, and use variables in styled-component ? i use styles for using less variables in styled-compnent but eslint and hot reloading is the problem – pooria Nov 09 '20 at 11:16
  • @pooria I don't understand your problem but it sounds like it is a different one and you should open another question. – JustTB Nov 10 '20 at 12:44
18

@zeit/next-less is deprecated and disables Next's built in CSS support. It also uses a very old version of less and less-loader.

I created a package that injects Less support to Next.js by duplicating the SASS rules and setting them for Less files and less-loader. It works with webpack5 flag.

https://github.com/elado/next-with-less

https://www.npmjs.com/package/next-with-less

elado
  • 8,510
  • 9
  • 51
  • 60
  • Thanks for the package. An alternative option would be using [next-plugin-antd-less](https://www.npmjs.com/package/next-plugin-antd-less) but it seems that for 21/04/2021 they don't support webpack5 yet. – Jakub Siwiec Apr 21 '21 at 12:39
  • 3
    That's mainly why I wrote my package (webpack5 support). Also, it maintains the exact behavior of Next's errors and file-loader. It's not Ant specific either. – elado Apr 21 '21 at 15:51
  • 1
    @elado that's awesome! Could you add an example to the README where you override some of `antd` variables? I tried adding `lessVarsFilePath` to `next.config.js` (like what we need to do for next-plugin-antd-less), but it has no effect. – LGenzelis Apr 28 '21 at 22:15
  • I created a [PR](https://github.com/elado/next-with-less/pull/2) for @elado's plugin, to add an option to customize antd variables. We'll see how it goes! :) – LGenzelis Apr 29 '21 at 16:30
  • This solution works perfectly with next.js 11.1.2, and node 14. – holibut Sep 26 '21 at 07:16
9

While the above answers may work for versions of NextJS lower than 11, they do not work for 11+. I've found excellent success with the following plugin...

https://github.com/SolidZORO/next-plugin-antd-less

Max Phillips
  • 6,991
  • 9
  • 44
  • 71
  • 2
    Thanks for the answer but it is also deprecated for the most recent NextJS version, for example version `12.1.6`. Because NextJS has adapted the use of SWC. I also need some help with the SWC configuration with NextJS and antd. If anyone can help it would be nice. – Oluwagbemi Kadri Jun 22 '22 at 09:53
2

To add Less to the Next.js is easy way.

Need to add 'next-with-less' library (also install less and less-loader) and 'next-compose-plugin'.

To your next.config.js add:

/** @type {import('next').NextConfig} */

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

const plugins = [
  [
    withLess,
    {
      lessLoaderOptions: {},
    },
  ],
];

module.exports = withPlugins(plugins, {
  reactStrictMode: true,
  swcMinify: true,
});
KarolinaK
  • 21
  • 1
1

I am using elado's package which is - https://github.com/elado/next-with-less

you will need less and less-loader as dependencies. after that create a global.less file on styles folder. so it's like ,root> style > global.less and paste this code

@import '~antd/lib/style/themes/default.less';
@import '~antd/dist/antd.less'; 
@primary-color: #ff9b18; 
@border-radius-base: 20px;

and add below code in your next.config.js file which you will create on your root folder.

   // next.config.js
const withLess = require("next-with-less");

module.exports = withLess({
    lessLoaderOptions: {
        /* ... */
    },
});
0

In our project, there were old scss and css files. They were not using the Next js guidline for CSS modules. So I had to override webpack.config.js. So that works fine. But when we moved the file to the monorepo shared package, babel was not transpiling them. I used the below things, but those did not work for SCSS modules without a .module extension.

  • next-transile-module.
  • experimental: { externalDir: true, } in next Js config with root babel.cofig.json

Finally symlink hack worked for external shared files

Use symlinks for shared folder by updating next.config.js

module.exports = {
  //...
  resolve: {
    symlinks: false,
  },
};
ouflak
  • 2,458
  • 10
  • 44
  • 49
Vipul Nema
  • 21
  • 3
0

For anyone who is still having trouble, you don't need any extra package other than our lovely mini-css-extract-plugin. Here is how you solve the issue.

PS: I also added sass to my webpack config. You can use both less and sass/scss files in your project.

next.config.js:

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

module.exports = {
  webpack(config) {
    config.module.rules.push(
      {
        // this part is for css
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, { loader: 'css-loader' }],
      },
      {
        // this part is for sass
        test: /\.module\.(scss|sass)$/,
        use: [
          MiniCssExtractPlugin.loader,
          { loader: 'css-loader' },
          { loader: 'sass-loader' },
        ],
      },
      {
        // this part is for less
        test: /\.less$/i,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
          },
          {
            loader: 'less-loader',
            options: {
              sourceMap: true,
              lessOptions: {
                javascriptEnabled: true,
              },
            },
          },
        ],
      }
    );

    config.plugins.push(
      new MiniCssExtractPlugin({
        filename: 'static/css/[name].css',
        chunkFilename: 'static/css/[contenthash].css',
      })
    );

    return config;
  },
};

package.json:

"dependencies": {
    "@next/font": "13.1.1",
    "css-loader": "^6.7.3",
    "less": "^4.1.3",
    "less-loader": "^11.1.0",
    "mini-css-extract-plugin": "^2.7.2",
    "next": "13.1.1",
    "next-transpile-modules": "^10.0.0",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "sass": "^1.57.1",
    "sass-loader": "^13.2.0",
  }
pkocak
  • 9
  • 2