React+Webpack1/2/3+Semantic UI and how to do theming

Semantic UI is a great UI framework which can make your UI development much easier, but it’s still not a joyful job to make it work with webpack especially for theming.

This tutorial works for both webpack 1/2/3, though you need to make small change just to follow webpack2/3’s syntax. For the main differences for webpack2/3, scroll to the bottom!

Use Semantic-UI without theming

If you just want to use Semantic-UI with your Webpack-React project, it’s quite easy.

You only need to follow two steps:

  • npm install semantic-ui-react --save (see semantic-ui-react)
  • Since the semantic-ui-react does not include style, you can use the default Semantic UI stylesheet by including a Semantic UI CDN link in your index.html file.
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.2/semantic.min.css"></link>

Just create a simple button in your code to test if it is working:

import React from 'react';
import { Button } from 'semantic-ui-react';

const ButtonExampleButton = () => (
  <Button>
    Click Here
  </Button>
)

export default ButtonExampleButton

Instead of using a Semantic UI CDN link, you can manually handle the CSS by yourself, just:

  • npm install semantic-ui-CSS --save
  • In your entry file, include the entry file:
// app.js
import 'semantic-less-css/semantic.css';

Remember to turn off the “CSS-Module” functionality for .css file. And notice that both using CDN or semantic-ui-css do not provide the theming feature.

Theming

To be able to use the theming feature, you need to use semantic-ui-less. It’s a pure less part of Semantic UI aiming to help you get rid of all the unnecessary dependencies from Semantic-UI.

npm install semantic-ui-less --save

And since it’s Less, and there are also Icon Fonts involved in this module, make sure your webpack can handle them well:

// webpack configs:
  .....
          loaders: [
            {
                test: /\.less/,
                loader: 'style!css!less',
            },
            {
                test: /\.(png|jpg|gif|woff|svg|eot|ttf|woff2)$/,
                loader: 'url-loader?limit=1024&name=[name]-[hash:8].[ext]!image-webpack',
            },
        ],
  ...

Before we start the work, let’s take a look at the project structure (you can check out the demo project):

app/
-- index.js
-- index.html
-- semantic-ui/   // all the semantic-ui related files
----- site/       // theming related files
webpack.config.js
package.json

rewrite less import for ../../theme.config

semantic-ui-less tries to import a file that does not exist( node_modules/semantic-ui-less/theme.config ), this file includes some key variables used throughout the whole module.

So first of all, you need to create a theme.config by yourself ( create a directory named semantic-ui in your project root):

app/semantic-ui
- theme.config

And copy the content of node_modules/semantic-ui-less/theme.config.example into theme.config and remember to do a import path fix, change @import "theme.less" to @import "~semantic-ui-less/theme.less":

Also don’t forget to change the @siteFolder to your folder:

@siteFolder  : '../../app/semantic-ui/site';

After that, we can use a Less plugin to make the ../../theme.config refer to our own theme.config. Let’s intall the module first:

npm install less-plugin-rewrite-import --save-dev

Then add the configuration to webpack.config.js:

const RewriteImportPlugin = require("less-plugin-rewrite-import");

... below is just part of the webpack config
    essLoader: {
        lessPlugins: [
            new RewriteImportPlugin({
                paths: {
                    '../../theme.config':  __dirname + '/app/semantic-ui/theme.config',
                },
            }),
        ],
    },
...

You can now import the main entry of semantic-ui-less from /app/index.js:

import "semantic-ui-less/semantic.less";

Anyway, you will still get build error complaining about that it can’t find font files.

Fix @fontPath error

What you need to do is just add one line of code showed bellow at the bottom of your theme.config ( make sure it’s bellow the line that imports theme.less ):

@fontPath : '../../../themes/@{theme}/assets/fonts';

Now the build process should work like a charm.

Setup customization for your site

Create extra structure for your semantic-ui directory:

/semantic-ui
- theme.config
- site/

Most of custom style code goes into site/ folder, the structure will be like this:

site/
- collections/
- elements/
- globals/
- modules/
- views/

To verify whether the customisation is working, just create /app/semantic-ui/site/elements/button.variables and change background color for all buttons:

@backgroundColor: yellow;

Put a button into your page /app/index.ejs to see if it works:

<div class="ui button">Button</div>
Yellow Button

If you see the same, it works.

Theming for Webpack2 / 3

For those working with Webpack2 or 3, except for general syntax change, there’re two differences you need to make for the theming to work. (Thanks for @MarkPerryBV’s idea from this issue)

Use less-resolver

By default, less-loader will use webpack’s resolver to resolve all the less files, making plugins like less-plugin-rewrite-import fail to handle less files. That’s why you will find the plugin not working for webpack2.

To make less-loader use its own resolver, you need to manually specify option paths for it to search, your config should be:

{
  loader: 'less-loader',
  options: {
    paths: [ROOT_DIR, NODE_MODULES_DIR], // this will force less-loader to use its own resolver, both should be absolute path
    plugins: [
      new RewriteImportPlugin({
        paths: {
          '../../theme.config':  __dirname + '/app/semantic-ui/theme.config',
        },
      }),
    ],
  },
},

You can get more details about less resolver from here

Remove ~ in your theme.config

Since now we are using less resolver, we are not able to use ~ to refer modules in the node_modules anymore, so open your theme.config and change the @import "~semantic-ui-less/theme.less"; to @import "semantic-ui-less/theme.less";

For more specific example, checkout my example project: demo-semantic-ui-theming-with-webpack2

References:

Tools & Services I Use

22 Comments

  1. Everything works great. But on last step, I dont see any effects. I checked and my site folder is not getting executed.

  2. Fixed it.
    In theme.config, I changed path of my site folder to (its relative from the module)
    @siteFolder : ‘../../semantic-ui/site’;

  3. Hey @neekey, I don’t suppose you have this working with Webpack 2? I have tried but for the life of me haven’t been able to get the rewrite import plugin working.

  4. Even if I added the reweriteImportPlugin, I still have the same error!

    ERROR in ./~/css-loader!./~/less-loader/lib/loader.js!./~/semantic-ui-less/semantic.less
    Module build failed: Cannot resolve ‘file’ or ‘directory’ ../../theme.config in /Users/jharvey/Serdy/www/fe-refonte/fe-client/node_modules/semantic-ui-less/definitions/modules
    @ /Users/jharvey/Serdy/www/fe-refonte/fe-client/node_modules/semantic-ui-less/definitions/modules/transition.less (line 19, column 0)
    near lines:

    @import (multiple) ‘../../theme.config’;

    @ ./~/semantic-ui-less/semantic.less 4:14-94 13:2-17:4 14:20-100

      1. Hi John, not sure if you have solved this issue already, just want you to know that I have found a solution and have updated the article. You can check out the project as well.

  5. Wow! Helped me lot to get this working. Thank you very much for this post.

    One little note: The `RewriteImportPlugin` does not work with less-loader >= 4.0.0. Downgraded for now to 2.2.3 to get it working.

    1. Hi Alban, not sure if you have solved this issue already, just want you to know that I have found a solution and have updated the article. You can check out the project as well.

  6. Ok, i fixed it by skipping that plugin and just using a postinstall in my package.json of

    “postinstall”: “node font-fix.js && cp ./src/app/styles/semantic-ui/theme.config ./node_modules/semantic-ui-less/theme.config”

    1. Yes, Ian, you can always do that, but the reason I wrote this post was that I wanted to get a more elegant way to achieve this, but as soon as it works for you, it’s good : )

    2. Hi Ian, not sure if you have solved this issue already, just want you to know that I have found a solution and have updated the article. You can check out the project as well.

  7. Can’t seem to find a workaround for ejected CRA project with webpack 4

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.