Customising Ant Design theme in a create-react-app project

There comes a time in some developers’ life when they have to customize an antd theme. It’s kind of documented, but tricky. Here’s a tl;dr.

But first… why antd?

Well, it looks like a very complete UI system. Creating your own is a lot of effort, so why not use something that’s out there and working perfectly? Of course, there are other alternatives, like Material UI or Evergreen.

With that out of the way, let’s jump right in.

CRA 2.0+ (most probably this is what you’ll need)

yarn add antd
yarn add @craco/craco craco-antd babel-plugin-import less less-loader

In package.json:

-  "start": "react-scripts start",
-  "build": "react-scripts build",
+  "start": "craco start",
+  "build": "craco build",

In App.(j|t)sx:

import 'antd/dist/antd.less';

Create craco.config.js in root:

const CracoAntDesignPlugin = require('craco-antd');

process.env.BROWSER = 'none'; // optional, disables auto-open every time you restart

module.exports = {
  jest: {
    configure(config) {
      config.transformIgnorePatterns = [
        '/node_modules/(?!antd|rc-pagination|rc-calendar|rc-tooltip)/.+\\.js$',
      ];
      return config;
    },
  },
  plugins: [{ plugin: CracoAntDesignPlugin }],
};

Theming

Craco will pick up a theme from antd.customize.less.1

 @primary-color: red;

Live style reload

Themes will not reload real-time by default. However, this is easy to solve with nodemon.

yarn add -D nodemon

Update package.json:

-  "start": "craco start",
+  "start": "nodemon -w craco.config.js -w ./antd.customize.less --exec 'craco start'",

CRA Pre-2.0 (if your project is older)

yarn add antd
yarn add babel-plugin-import customize-cra less less-loader react-app-rewired

In package.json:

-  "start": "react-scripts start",
-  "build": "react-scripts build",
-  "test": "react-scripts test",
+  "start": "react-app-rewired start",
+  "build": "react-app-rewired build",
+  "test": react-app-rewired test",

In App.(j|t)sx:

import 'antd/dist/antd.less';

Create config-overrides.js in root:

const { override, fixBabelImports, addLessLoader } = require('customize-cra');

module.exports = override(
  fixBabelImports('import', {
    libraryName: 'antd',
    libraryDirectory: 'es',
    style: true,
  }),
  addLessLoader({
    javascriptEnabled: true,
    modifyVars: {
      '@primary-color': 'red',
    },
  }),
);

Advanced theming

Nobody in your team will look for themes in config-overrides.js however, so let’s extract the theme somewhere where it makes sense.

Create a theme.js with a simple object exported:

module.exports = {
  '@primary-color': 'red',
};

Now replace the theme changes in config-overrides.js:

const { override, fixBabelImports, addLessLoader } = require('customize-cra');
+ const theme = require('./path/to/theme');

module.exports = override(
  ...
  addLessLoader({
    javascriptEnabled: true,
-    modifyVars: {
-      '@primary-color': 'red',
-    },
+    modifyVars: theme,
  }),
);

Notes


  1. See all customisable variables here.