Select Page

Understanding Magento Theme: Use grunt js for better development in Magento 2

Rahul Chaudhary
Published: July 29, 2022

Hello Friends, Today We’ll go over how to use Magento’s out-of-the-box commands in Grunt, and how to add our own (custom grunt configuration) to make our development faster.


You can use Grunt to automate any tasks you need, but out of the box Magento comes with pre-configured grunt tasks for compiling LESS files.


There are only two major requirements. First of all, you have to set your Magento 2 to the developer or default mode. Note that with the default mode, you set the Less compilation mode to Server-side Less compilation, which is required for our final goal.

Installing and configuring Grunt

Magento has built-in Grunt tasks configured, but there are still several steps you need to take to be able to use it:

Install node.js to any location on your machine.

Searching for an experienced
Magento 2 Company ?
Read More

Install Grunt CLI tool globally. To do this, run the following command in a command prompt

npm install -g grunt-cli

If you get an error that permission is denied, you’ll need to run the command as sudo or on Windows, you’ll need to run PowerShell or Command Prompt as an administrator.

Installation of grunt-cli

Go to the <Magento_root> directory, copy and paste the contents of the following files:

  1. package.json.sample into package.json
  2. Gruntfile.js.sample into Gruntfile.js
  3. grunt-config.json.sample into grunt-config.json

Install (or refresh) the node.js project dependency, including Grunt, for your Magento instance. To do this, run the following commands in a command prompt:

cd your_Magento_instance_directory
npm install
npm update

There is also an optional step related to Grunt for “watching” changes automatically, without reloading pages in a browser each time. To implement this feature, you have to install the LiveReload extension.

Adding themes to Grunt configuration

Add your Custom Theme to Grunt Configuration open this file, in the dev/tools/grunt/configs/themes.js and add your theme to module.exports like shown below:

module.exports = {
     <theme>: {
         area: '<area>',
         name: '<Vendor>/<theme>',
         locale: '<language>',
         files: [
             '<path_to_file1>', //path to root source file
         dsl: 'less'

Where the following notation is used:

  • <area>:- area code, can be either frontend or adminhtml.
  • <vendor_name>:- Your vendor name.
  • <theme_name>:- Your theme code that corresponds to the theme directory name.
  • <language>:- language specified in the code_subtag format, for example en_US. Only one locale can be specified here. To debug the theme with another locale, create one more theme declaration, having specified another value for language.
  • <path_to_file >:- The path to the root source file, relative to the directory app/design/frontend/<Vendor>/<theme/>/web.

NOTE: If grunt gives the error “Error: Cannot find module ….”, check the path in your grunt-config.json file and correct as necessary.

Grunt commands

You just need to execute commands :

  • grunt clean :- Remove the theme-related static files in the pub/static/frontend/ and var/ directories.
  • grunt exec:- To generate symlinks to the source files to pub/static/frontend/
  • grunt less:- Compile CSS files using symlinks published in pub/static/frontend/
  • grunt watch:- To track changes in source files, recompile .css files, and reload pages in a browser.

Grunt commands for specific theme

  If you are using for a specific theme then execute commands :

  • grunt exec : <theme_name> :- To generate symlinks for a specific theme.
  • grunt less : <theme_name> :- To use symlinks published for a specific theme.

After running “grunt watch” will watch the *.less files and if some are changed, it will immediately rebuild the CSS file.

Custom Grunt configuration files

There are several ways to declare a custom configuration file.

How to declare a custom config file 

There are two options that let you declare a custom config file in a different manner.

Option 1

  1. Copy the default configuration file to the preferred location in the Magento instance directory. Do not change the default file name.
  2. Open the grunt-config.json file in the Magento root and set configurations object as follows.
    • key: file alias
    • value: path to your custom file
  3. If your custom configuration file local-themes.js is located in the <magento_root>/dev/tools/grunt/configs directory, the following is already set in your grunt-config.json
{ "themes": "dev/tools/grunt/configs/local-themes.js" }

This path is also added to your .gitignore by default. If you are changing this name, It is also necessary to mention that this path is added to your .gitignore.

Option 2

You can also use the other way to declare a custom config file:

In your Grunt related scripts, in the file router, set the alias and path to the custom configuration file. For example, to set the custom themes.loc.js configuration file, this would look like the following:

filesRouter.set('themes', 'dev/tools/grunt/configs/themes.loc.js');

Note that It must be added earlier, than the get() method with this alias is called.

In the dev/tools/grunt/configs/ directory, create a copy of the default configuration file. Change its name by adding the “.loc” suffix.

For example, your copy of themes.js will be themes.loc.js.

How to use a custom configuration file

Now you have to tell Grunt to use a custom configuration file, instead of the default one, add the following in your script:

  1. Require file-router:
var fileRouter = require('/files-router');

2. Call the get(%file_alias%) method to get the configuration file.

var themes = fileRouter.get('themes');

Adding Custom Tasks

Now we’ll go over how to create custom tasks in Grunt that will help make our development faster.

One use case we can automate is when we update a module’s version. Usually, you have to run 3 different tasks:

php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento setup:static-content:deploy -f

Instead of running the above commands, we’ll create a new task called upgrade that will run all these tasks for us.

To create a new task, we need to create a new file in dev/tools/grunt/tasks. We’ll create a new file there called upgrade.js

The file should export a function that takes grunt as a parameter:

module.exports = function(grunt) {
	//Write code here for the custom task

The reason behind this is that Gruntfile.js fetches all files in the tasks directory, and passes them the instance of grunt.

Now, we’ll declare some functions that will be helpful for us:

const exec = require('child_process').execSync,
        log = grunt.log.write,
        ok = grunt.log.ok
  1. exec: It’s actually the function execSync that allows us to run commands we would run in the shell. We’ll use it to run the commands mentioned above.
  2. log: A function that allows us to output information messages.
  3. ok: A function that allows us to output successful messages.

This time to register our task, we’ll use grunt.registerTask which takes two parameters: the name of the task and the function that the task will execute once called.

grunt.registerTask('upgrade', function () {

The first parameter is the command to run, and the second parameter is an options object. The option we’re passing is stdio with the value inherit, which means that the output should be printed to the terminal we’re calling the task from.

We run a grunt task with passing it the name of the task. In the end, we’re just outputting a successful message to show that our task is done.

Final code of upgrade.js should look like this:

module.exports = function(grunt) {
    const exec = require('child_process').execSync,
        log = grunt.log.write,
        ok = grunt.log.ok
    grunt.registerTask('upgrade', function () {
        log('Running setup:upgrade...')
        exec('php bin/magento setup:upgrade', {stdio: 'inherit'})
        log('Running setup:di:compile')
        exec('php bin/magento setup:di:compile', {stdio: 'inherit'})
        log('Running deploy...')'deploy')
        ok('Upgrade finished!')

Finally, test it out. In the terminal run:

grunt upgrade

If everything is done correctly,  the task will run all mentioned commands. This task will make it easier next time you need to upgrade because of a new or updated module! Now, you can automate any task with the same process.

Happy Coding! 🙂