Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Theming Ant Design with Sass and Webpack

Theming Ant Design with Sass and Webpack

This is a solution on how to theme/customize Ant Design (which is written in Less) with Sass and webpack. Ant itself offers two solutions and a related article on theming, but these are only applicable if you use Less, the antd-init boilerplate or dva-cli.

What this solution offers:

  • use a single sass-file to customize (no duplicate variables for your project and Ant)
  • hot reload compatibility
  • no dependencies on outdated npm modules
  • easy integration with your existing webpack setup (webpack 3+ tested)

Any downsides?

  • all ant-styles are imported

According to our observations this does not have that much impact in practice, as using only a couple of Ant components will already result in most styles being loaded.

Procedure

The main procedure is as follows:

  1. Define your variables in variables.scss
  2. Write a ant.less file which imports the Ant less-styles and your sass-variables (yes)
  3. Tell webpack to load less files via less-loader
  4. Tell webpack to rewrite sass-files which are imported from less-files ($something -> @something)
  5. Import ant.less in your project.

How?

Apply the changes / create the files as shown below. The changes should be self-explanatory. Just keep in mind, that your exact webpack-setup may differ a bit from the one given below.

Authors: Kruemelkatze, mrukas

//Use .babelrc or add the options to the webpack loader
//Remove comments in .babelrc as they most likely will produce build errors
{
"presets": [
//...
],
"plugins": [
//...
["import", {
"libraryName": "antd",
"libraryDirectory": "es" //or "lib" for default
//No "style" setting
}]
]
}
@import "~antd/dist/antd.less";
@import "variables.scss"; // <-- SASS in a LESS file!
// Import the ant.less file in your project
import 'ant.less';
// This loader will simply replace all $something sass-variable with @something less-variables
module.exports = function (source) {
return source.replace(/\$/ig, '@');
};
// THIS FILE SHOULD ONLY CONTAIN VARIABLE DEFINITIONS
// Ant Colors & Styles can be overwritten here. Just use the variable names from
// https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less
// Be sure to use $xyz, not @xyz!
$primary-color: crimson;
module: {
rules: [
{
loader: 'babel-loader',
test: /\.jsx?$/,
// Other settings, like include or exclude
options: {
presets: [
//...
],
plugins: [
// ...
// Importing Ant here is not needed if you are using a .babelrc file
['import', {
"libraryName": "antd",
"libraryDirectory": "es" // or "lib" for default
// No "style" setting
}],
]
},
},
// ...
// Include less-loader (exact settings may deviate depending on your building/bundling procedure)
{
test: /\.less$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" },
{
loader: "less-loader",
options: {
javascriptEnabled: true //This is important!
}
}
]
},
// Tell the DEFAULT sass-rule to ignore being used for sass imports in less files (sounds weird)
{
test: /\.scss$/,
issuer: {
exclude: /\.less$/,
},
// ... other settings
},
// Define a second rule for only being used from less files
// This rule will only be used for converting our sass-variables to less-variables
{
test: /\.scss$/,
issuer: /\.less$/,
use: {
loader: './sassVarsToLess.js' // Change path if necessary
}
},
// ...
]
}
@tim-soft

This comment has been minimized.

Copy link

@tim-soft tim-soft commented Mar 28, 2018

Hey that's pretty clever! I integrated this into a boilerplate to test with and seems to work pretty well, have you ran into any stability issues/crashes during HMR?

Here's a demo for anyone interested
https://github.com/tim-soft/react-starter-kit-antd

git clone https://github.com/tim-soft/react-starter-kit-antd.git
cd react-starter-kit-antd
yarn install && yarn start
@Kruemelkatze

This comment has been minimized.

Copy link
Owner Author

@Kruemelkatze Kruemelkatze commented Mar 30, 2018

Hey, thanks! Glad that some people are using this! :)

We (8 devs in our company) did not encounter any issues with HMR yet. It may take a bit longer if the variables are changed because the Ant styles are recompiled. But changing branding colors isn't anything that you do on a regular basis :)

@Hazantip

This comment has been minimized.

Copy link

@Hazantip Hazantip commented Apr 12, 2018

Thank's a lot, this magic works good!

@tkharuk

This comment has been minimized.

Copy link

@tkharuk tkharuk commented Jun 11, 2018

This way, antd styles will be loaded after your component's styles, right?
If so, there will be no possibility to overwrite some pesky !importants :(

@Kruemelkatze

This comment has been minimized.

Copy link
Owner Author

@Kruemelkatze Kruemelkatze commented Jun 21, 2018

We included our styles in this order and do not have any issues with overriding:

// App.jsx
import 'ant.less';
import 'main.scss'; // our app styles

Before we switched to this solution, we loaded the Ant styles using Ant's import-loader. With this we had the exact same problem you describe, so we used a (really dirty!) hack and reversed the css chunks generated by HtmlWebpackPlugin.

// webpack.config.js
plugins: [
    new HtmlWebpackPlugin({
        // ...
        chunksSortMode: function (a, b) {
            // Reverse files in css chunk
        }
    }
]

I did not post the whole code as we strongly discourage this hack. ;)

@ZhangYiJiang

This comment has been minimized.

Copy link

@ZhangYiJiang ZhangYiJiang commented Jul 3, 2018

How does this compare to using https://github.com/mediafreakch/less-plugin-sass2less instead? That seems to be less hacky and supports eg. mixins if you need to use them to define the SASS variables

@JabFury

This comment has been minimized.

Copy link

@JabFury JabFury commented Jul 6, 2018

var content = require("!!../../../node_modules/_css-loader@0.28.11@css-loader/index.js!../../../node_modules/_sass-loader@7.0.3@sass-loader/lib/loader.js!./variables.scss");
^
Unrecognised input

When I try to npm run dev, my terminal show this message for me,I don't know how to solve this

@atav32

This comment has been minimized.

Copy link

@atav32 atav32 commented Jul 17, 2018

@JabFury, looks like you might be missing the sass-loader?

Nvm, I got this error too. It's because my webpack config is the one that comes with create-react-app and I put the second Sass rule in the wrong place. I'm able to fix that bug if I make sure the rule is outside the oneOf rule.

  ...
  modules: {
    rules: [
      ...
      {
        oneOf: [
          // default loaders
          ...
        ]
      },
      {
        test: /\.scss$/,
        issuer: /\.less$/,
        use: {
          loader: require.resolve('./sassVarsToLess'),
        }
      }
    ]
  },
  ...
@wurendedadi

This comment has been minimized.

Copy link

@wurendedadi wurendedadi commented Sep 30, 2018

thx, I really fix my problem by your solution

@funewik

This comment has been minimized.

Copy link

@funewik funewik commented Jan 22, 2019

Thank you very much for this procedure. A question though, the fact we load all styles from AntD, it crushes all of my reset.scss, put margin and padding everywhere, it's kind of impossible to control anything.
Is it possible to avoid this please ?

And also, compile is a way longer than before when it's time to hot-reload...

@funewik

This comment has been minimized.

Copy link

@funewik funewik commented Jan 22, 2019

Ok, so i answer my own question :)

In ant.less, instead of import all styles, you have to remove base.less by doing this :
Replace :

@import '~antd/dist/antd.less';

By :

@import '~antd/lib/style/mixins/index.less';
@import '~antd/lib/style/core/iconfont.less';
@import '~antd/lib/style/core/motion.less';
@import '~antd/lib/style/components.less';
@samcyn

This comment has been minimized.

Copy link

@samcyn samcyn commented Apr 3, 2019

Hi I tried to implement this in nuxt js and it didn't work. Is this only working with react and next js?

@Kruemelkatze

This comment has been minimized.

Copy link
Owner Author

@Kruemelkatze Kruemelkatze commented Jun 14, 2019

Hi I'm tried to implement this in nuxt js and it didn't work. Is this only working with react and next js?

Hey, sorry for the late answer. We did not use Next js, Nuxt js or any other frameworks. Only webpack + React + Antd

@vrsotto

This comment has been minimized.

Copy link

@vrsotto vrsotto commented Sep 7, 2019

can i Use this with create-react-app?

@mrukas

This comment has been minimized.

Copy link

@mrukas mrukas commented Sep 7, 2019

Yes it is possible to use it with create react app. I'm not sure if there is another way than ejecting (https://create-react-app.dev/docs/alternatives-to-ejecting). Maybe you can customize the webpack.config otherwise. But ejecting should definitely work. Please make sure you know what you do before ejecting.

@antoinm

This comment has been minimized.

Copy link

@antoinm antoinm commented Oct 4, 2019

@vrsotto I just managed to get it working with CRA TS.

Here is what I've done:

  • Install react-app-rewired, customize-cra, less and less-loader
  • Modify the scripts in the package.json to use react-app-rewired
  • Create a config-override.js just like this:
const {
  override,
  fixBabelImports,
  addLessLoader,
  addWebpackModuleRule
} = require("customize-cra");

module.exports = override(
  fixBabelImports("import", {
    libraryName: "antd",
    libraryDirectory: "es",
    style: true
  }),
  addLessLoader({
    javascriptEnabled: true
  }),
  addWebpackModuleRule(
    // This rule will only be used for converting our sass-variables to less-variables
    {
      test: /\.scss$/,
      issuer: /\.less$/,
      use: {
        loader: "./sassVarsToLess.js" // Change path if necessary
      }
    }
  )
);
  • Follow the steps listed in the original post BUT skip the .babelrc and webpack.config.js files.

I hope this will help you.

@vrsotto

This comment has been minimized.

Copy link

@vrsotto vrsotto commented Oct 5, 2019

@vrsotto I just managed to get it working with CRA TS.

Here is what I've done:

  • Install react-app-rewired, customize-cra, less and less-loader
  • Modify the scripts in the package.json to use react-app-rewired
  • Create a config-override.js just like this:
const {
  override,
  fixBabelImports,
  addLessLoader,
  addWebpackModuleRule
} = require("customize-cra");

module.exports = override(
  fixBabelImports("import", {
    libraryName: "antd",
    libraryDirectory: "es",
    style: true
  }),
  addLessLoader({
    javascriptEnabled: true
  }),
  addWebpackModuleRule(
    // This rule will only be used for converting our sass-variables to less-variables
    {
      test: /\.scss$/,
      issuer: /\.less$/,
      use: {
        loader: "./sassVarsToLess.js" // Change path if necessary
      }
    }
  )
);
  • Follow the steps listed in the original post BUT skip the .babelrc and webpack.config.js files.

I hope this will help you.

Thanks @antoinm but i got it working through this blog https://dailyjsx.com/antd-rescript-sass-boilerplate/

@AlexeyDrobot

This comment has been minimized.

Copy link

@AlexeyDrobot AlexeyDrobot commented Jan 30, 2020

Thanks for this way. Is there any way to avoid duplication of styles? https://take.ms/8vOAP

@Kruemelkatze

This comment has been minimized.

Copy link
Owner Author

@Kruemelkatze Kruemelkatze commented Jan 30, 2020

Thanks for this way. Is there any way to avoid duplication of styles? https://take.ms/8vOAP

Hmm, we did not encounter this in our apps. Do you maybe still have the antd styles imported a second time somewhere in your .js or .jsx?

@AlexeyDrobot

This comment has been minimized.

Copy link

@AlexeyDrobot AlexeyDrobot commented Jan 30, 2020

I did it on a clean project. create-react-app
And I was convinced that I connect ant.less once. No more. Any other ideas?

@adomrockie

This comment has been minimized.

Copy link

@adomrockie adomrockie commented Feb 18, 2020

I did it on a clean project. create-react-app
And I was convinced that I connect ant.less once. No more. Any other ideas?

Did you get this to work? Having the same issue.

@Kruemelkatze

This comment has been minimized.

Copy link
Owner Author

@Kruemelkatze Kruemelkatze commented Feb 18, 2020

We will have a look on this and then report here! :)

@adomrockie

This comment has been minimized.

Copy link

@adomrockie adomrockie commented Feb 19, 2020

We will have a look on this and then report here! :)

Thanks, must be something we over looking but I have checked my code base and I only use ant.less once.

@anupsarode

This comment has been minimized.

Copy link

@anupsarode anupsarode commented Apr 8, 2020

Do we have any working example using NextJs framework? I am trying to use the in-built Sass feature available in NextJs 9.3 and need to convert Ant's less variables to Sass variables.

@Kruemelkatze

This comment has been minimized.

Copy link
Owner Author

@Kruemelkatze Kruemelkatze commented Apr 8, 2020

We will have a look on this and then report here! :)

Thanks, must be something we over looking but I have checked my code base and I only use ant.less once.

Sorry for not getting back to you! Was quite some troubling time, but we will check this. Unfortunately, we stopped using this approach in our company, as we migrated to an unejected CRA boilerplate and didn't use theming that much anyway. So I'm not totally up to date on this. ;)

Do we have any working example using NextJs framework? I am trying to use the in-built Sass feature available in NextJs 9.3 and need to convert Ant's less variables to Sass variables.

I haven't used NextJs before, sorry. But it looks like they allow for a custom webpack config and babel config Maybe you can add the rules and loaders there?

@anupsarode

This comment has been minimized.

Copy link

@anupsarode anupsarode commented Apr 8, 2020

Do we have any working example using NextJs framework? I am trying to use the in-built Sass feature available in NextJs 9.3 and need to convert Ant's less variables to Sass variables.

I haven't used NextJs before, sorry. But it looks like they allow for a custom webpack config and babel config Maybe you can add the rules and loaders there?

Thanks for the quick revert. I tried configuring the webpack with NextJS, but the moment we less-loader in the config, NextJs disables the inbuilt sass support. I guess I should ask the NextJs team if they can come up with a workaround for this one. Thanks for your time :)

@pabloimrik17

This comment has been minimized.

Copy link

@pabloimrik17 pabloimrik17 commented Jun 4, 2020

My build is failing since i updated to less-loader@6.1, in the older version (5.0) was working fine.

image

Any idea?

@Kruemelkatze

This comment has been minimized.

Copy link
Owner Author

@Kruemelkatze Kruemelkatze commented Jun 6, 2020

It looks like the sass-loader is not correctly called by the less-loader when processing the sass file. I had a look at the less-loade changelog for any obvious breaking changes but didn't see anything.

Do you still have this rule in your webpack config? Asking just in case if it got removed unintentionally when upgrading your project. :-)

    // Define a second rule for only being used from less files
    // This rule will only be used for converting our sass-variables to less-variables
    {
      test: /\.scss$/,
      issuer: /\.less$/,
      use: {
        loader: './sassVarsToLess.js' // Change path if necessary
      }
    },

Any idea on this @mrukas?

@pabloimrik17

This comment has been minimized.

Copy link

@pabloimrik17 pabloimrik17 commented Jun 17, 2020

It looks like the sass-loader is not correctly called by the less-loader when processing the sass file. I had a look at the less-loade changelog for any obvious breaking changes but didn't see anything.

Do you still have this rule in your webpack config? Asking just in case if it got removed unintentionally when upgrading your project. :-)

    // Define a second rule for only being used from less files
    // This rule will only be used for converting our sass-variables to less-variables
    {
      test: /\.scss$/,
      issuer: /\.less$/,
      use: {
        loader: './sassVarsToLess.js' // Change path if necessary
      }
    },

Any idea on this @mrukas?

Hi, everything in my project remains the same, i only updated from antd 4.0.2 to 4.3.4 and less-loader from 5.0.0 to 6.1. And related to less-loader changed the options config to lessOptions in the webpack file.

image

@jgroh9

This comment has been minimized.

Copy link

@jgroh9 jgroh9 commented Aug 25, 2020

@pabloimrik17 I'm having the same issue. Were you able to solve the problem? If so, would you be able to post the solution?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.