My guess is that if you’ve stumbled across this article you’ve probably already heard of Grunt, but maybe you’ve not used it much before or are a complete beginner, or maybe you’re wondering how you can use it as part of your Magento front-end workflow. This article will take you through that process, step by step.
I’ve written this article in the context of using Grunt with Magento, but to be honest the principles are the same regardless of the type of project you’re working on - you could easily just ignore the Magento stuff.
Magento isn’t the easiest of ‘beasts’ to work with, but with a bit of forethought and prep you can quite easily implement an automated front-end workflow. If that interests you then read on...
What we’re going to cover
- Initial Magento setup
- Creating a (symlinked) theme
- Setting up version control (Git)
- Adding some theme files
- Setting up a Grunt environment
- Adding Grunt tasks
- Watching for changes in XML and PHTML files
1. Initial Magento setup
Create project directory
From now on, unless specified otherwise, it will be assumed that all directory references and terminal commands are relative to magento-grunt-proj/.
Create shop root directory
Install Magento in shop root
If you’re reading this article I’m going to assume that you’re already familiar with installing and setting up Magento. However, here’s a handy tip - check out magerun. It provides developers with a number of useful Magento command line tools, including a brilliantly simple automated install script.
Set up local virtual host
As with installing Magento, I’m also going to assume that you know how to set up a local virtual host for your site.
Once you’re set up Magento, your project directory should look something like this:
2. Creating a (symlinked) theme
One thing you’ll notice straight away about this process that’s a bit different is that we’re going to put our theme files outside of our Magento shop root. The reason for this is that we basically want to treat our theme as a separate, contained project that we can commit to our Git repo without all the other Magento files. It also makes creating and maintaining our Gruntfile much easier.
It’s worth pointing out though, that you should only symlink your theme in a development environment and not in a live environment as it’s considered insecure - your deployment process would include steps that clone the theme’s repo, runs Grunt and then rsyncs the files/folders into the relevant places.
Create theme folder
Back in the root of your main project directory create the layout and skin folder structure for your theme:
Allow and create symlinks
In order to locate our theme files/folders in this non-standard location outside of our shop root we have to tell Magento to allow symlinks, and then create them.
Log in to Magento admin, go into System > Configuration > Developer > Template Settings and set Allow Symlinks to Yes.
Then on the command line:
What’s happening here is that we’re going into the standard Magento layout and skin folders and creating symlinks back to our external theme folders.
If you now show the contents of the two frontend directories above (
ls -al) you’ll see the created symlinks:
Activate your theme
Once you’ve done the above you’re ready to activate your theme. In System > Configuration > General > Design and enter the following details:
Current Package Name: my-theme
Skin (Images / CSS): default
TIP: At this point it’s probably a good idea to delete and turn off the Magento cache while you’re developing your theme. If you’re using magerun you can do this really easily from the command line:
All being well, if you now visit your site’s homepage you should see an unstyled page (because we haven’t added any kind of styling to our theme yet). If you get an error saying something about your package not being found it’s probably because you have a typo in one of your folder names (I’ve made this mistake many times), or created one of the symlinks incorrectly.
3. Setting up version control (Git)
If you’re just interested in getting to the good stuff and don’t really care about version control yet, you can skip forward to 4. Adding some theme files
Now is probably a good time to set up some version control. Here’s how to do it if you use Github.
For the sake of this tutorial we don’t want Git to track every file as we’re only interested in our theme files. Each person’s development environment is different anyway, so you’d want to tailor things to your specific requirements. So, create a .gitignore file in your project root directory:
Then in your code editor add the code below to the new .gitignore file - this basically tells Git to exclude pretty much everything but our project and theme specific files/folders:
Next create a README.md file. You can do this simply on the command line:
On a private repo you may wonder what is the point of creating a README.md file? Here at GPMD we use it on our own projects as a central reference point for all our developers - if there’s something specific they need to know about the setup of a site for example.
Now create your remote repo on Github(log into your Github account, click the Create new repo button and follow the instructions).
Once you’ve done that you’re ready to initialise your repo locally and push it to the remote Github repo (NOTE: you’ll need to enter your own Git username and repo where indicated):
You’ll also find the above instructions on your (currently) empty Github repo page - They’re very helpful like that.
Once Git has done its thing and pushed to Github try refreshing your Github repo page - you should see that it now contains some files/folders. However, if you’ve been paying attention you’ll also notice that our theme/ folder isn’t there. That’s because we haven’t added any files to our theme yet - Git doesn’t track empty folders.
4. Adding some theme files
At this point we can actually start setting up our theme, finally! There are, of course, many options open to you - You could use a boilerplate, such as this one based on Bootstrap 3 (we often use PolarCore based on Foundation, although it’s not free). Or you could, if you’re feeling particularly adventurous, start completely from scratch. However, for the sake of this tutorial I’m just going to use the existing Magento ‘modern’ theme as the starting point for my own theme.
We can quickly duplicate all the files/folders we need using the command line:
Now if you refresh the homepage of your site you should see if styled using the lovely *cough* Magento ‘modern’ theme, albeit with some missing assets (which, for the purposes of this article, we can ignore).
As above, you can ignore the next Git bit if you want and just move on to 5. Setting up a Grunt environment
Let’s add all our new files to Git.
If you refresh your Github repo page you should now see the theme/ folder.
5. Setting up a Grunt environment
Fantastic, this is where it starts to get fun! But before we can create some actual Grunt tasks we need to make sure our dev environment has everything it needs.
In order to use Grunt you’ll need to install Node.js first - Simply download the package and install it like you would any other app. Node is basically the ‘engine’ that makes everything we want to do, work.
Install the Grunt Command Line Interface (grunt-cli)
Then you’ll need to install the Grunt Command Line Interface (
grunt-cli), so we can, er, interface with Grunt using the command line:
This command uses Node package manager to install node modules, in this case,
grunt-cli. We’re installing it globally (
-g) so that we can run it from anywhere.
A bit later on we’re going to set up a CSS pre-processing Grunt task. In my example I’m going to use Compass (A CSS framework that uses Sass/SCSS), so you’ll need to make sure you have Compass installed. If you just want to use vanilla Sass without Compass then you’ll need to make sure you have Sass installed instead.
Create package.json for Node.js
As a bare minimum you need a package.json file for Node.js in the root directory of your project. Create package.json and enter the following:
This defines the name of your project, the project version number, and the fact that you want to install Grunt as a Node module (version 0.4.2 or whichever is the latest version if higher than 0.4.2).
Now go to the command line and run:
You should see a load of output (ignore warnings about ‘no description’, ‘no repository field’ and so on) and if you now look in your project folder you should see a new folder called node_modules/, which contains a folder called grunt/. Success, you’ve installed the Grunt Node module for your project!
6. Adding Grunt tasks
We’ve done a lot of cool stuff so far already, but now it’s going to get really interesting as we add our first Grunt tasks!
Add CSS pre-processing task
First of all we need to install the Grunt Node module that we want to use to do our pre-processing. As I mentioned before, I’m going to use Compass (the module is called grunt-contrib-compass), but if you want to just use vanilla Sass there’s a module for that too, called grunt-contrib-sass. Here’s how to install the Compass module in our project:
Once you’ve run this command, open package.json and you’ll see a new line has been added to the
You should also see a new folder in node_modules/, called grunt-contrib-compass/.
But wait a minute, at the moment we don’t have any Sass/SCSS files to do any pre-processing on. Let’s create some - The Magento ‘modern’ theme contains a few CSS files, which we can convert into SCSS files and then have Grunt compile them into CSS for us.
First of all create a new folder for your SCSS files:
Then duplicate the CSS files into this directory:
Then rename the suffix on each of these files to .scss:
NOTE: Be careful with the second command above - make sure you’re in the right directory or you could end up renaming a lot more files than you mean to.
If you look in the scss/ folder now you should see that all (four) CSS files have .scss suffixes.
Now we can set up the actual Grunt task. In order for Grunt to work we need to tell it what we want it to do each time it’s run, and we put that information in a file called Gruntfile.js. Go ahead and create this file in the root directory of your project, then open it and enter the following:
First of all I’ve defined the skin/ and app/ directories (to avoid a lot of re-typing later). Then I’ve told it where the package.json file is.
Next, I’ve set up some simple config for Compass, by telling it where my SCSS files are and where it should put the compiled CSS files (NOTE: see how I’ve used my
skinDir variable), what my working environment should be (
production), and what I’d like my output style to be. There are a lot more config options available, which you can read more about here, but these are really the only ones we need to get started. If you’re using grunt-contrib-sass then the syntax is slightly different - refer to the Github page for some examples, but it’s very similar and quite straightforward.
If you look a bit further down the code you’ll see I’ve told it to load the task
And finally I’ve registered the
compass task to run when the default
grunt task is run:
Now go to your command line and run:
You should see some output in your terminal that looks like this:
“Done, without errors.” Nice! In fact you’ll probably notice that Compass hasn’t actually done anything because nothing has changed between the SCSS and CSS files (they’re basically the same files at the moment as we simply copied them). In order to see Compass in action open up styles.scss and make a simple change - maybe change the background-color on the body tag to red? Run grunt again and this time the styles.scss line in your terminal output should say something like this:
Awesome, we can now see that styles.css has been overwritten! If you now open styles.css you should see that the layout has changed a bit - it’ll look a bit more bunched up (that’s the
nested option) and there will now be comments telling you where the source of that particular definition group comes from (that’s the
development option, which is really useful for debugging). If you set
production and the
compressed all the comments will disappear and the CSS will be on one line - pretty cool!
Add some automation with grunt-contrib-watch
All this is well and good, but we can go one better - how about we create a task that will automatically run the Compass task if it detects a change to any of the .scss files?
For this we’ll need the grunt-contrib-watch module. Install it in the same way that you installed the
You can check this has worked by looking in package.json and the node_modules/ folder.
Then add the following config to Gruntfile.js after your
compass task config:
This is basically telling the
watch task to keep an eye on the scss/ folder, and if it detects any changes to any of the .scss files to run the
Finally, tell it to load the
grunt-contrib-watch module or it won’t work:
To use the
watch task you need to run the following on your command line:
All being well you should see this:
This means it’s waiting for you to do something. Try making another change to styles.scss, save it, and see what happens - suddenly you’ll see the terminal spring to life and it will say something like this:
TIP: To stop the watch task from running (on a Mac at least) type
Add a live browser-refresh task (LiveReload)
But wait, we can go even better! You may have heard of a browser plugin called LiveReload (go ahead and install the plugin if you haven’t already)? We can use LiveReload to refresh the web page and see our changes happen as we make them, and the best part is that this functionality is already built into the
grunt-contrib-watch module - I’m going to change the watch task config a bit, so copy and paste it again:
So we’ve enabled livereload, Then we’ve told it to watch for changes in our scss/ folder and if it detects any, to run the
Now run the
grunt watch task on the command line, go to a page on your site, make sure LiveReload is enabled (you’ll probably get a LiveReload error if you try enabling it before you run
grunt watch), and then make a change to the styles.scss file. You should see the terminal spit out a load of stuff and the browser window refresh to show your changes (I’m not sure why it shows styles.scss being reloaded twice - one for the Grunt pros out there, but it works?) - it’s like magic or something!
Add image optimisation using grunt-contrib-imagemin
Another thing worth adding is an image optimisation task. The brilliant thing about Grunt is that if you can think of something you want to do there’s probably a module for it already, and in this case it’s called grunt-contrib-imagemin.
However, before we install the module, we need to duplicate the existing image folder/files. This is because we don’t want the location of our optimised files to be somewhere different - you’d then have to go through all the css and templates and change all the image path references to the new optimised image path - boring (and time-consuming)! Instead, we’ll create a new folder for our original (source) image files and then save the optimised versions back into the existing images/ folder. Makes sense? It’ll become clearer as you following along with me...
We’ve already used the command line to duplicate some folders, so let’s do the same again:
Now you should have two identical image folders, one called images/ and the other called images-src/.
Then, as before, let’s install the necessary Grunt module:
Check it’s there in the Node modules folder and package.json, and then register it in our Gruntfile:
Add the task configuration below:
This is telling the task where our original (source) images are, what type of files to optimise, and where to save them when they’ve been optimised.
And finally add the task to our
default set (Let’s start putting them on individual lines as it will become easier to follow as we add more tasks):
grunt on the command line and watch it squish all those images (it might take a couple of seconds as there are quite a few)! Here’s a snippet from my terminal output.
If you’ve been following along you can probably already guess what I’m going to suggest we do next? That’s right, we’re going to add the
imagemin task to our
watch task. Here’s our
watch config now:
All I’ve done here is to tell
livereload to watch the images-src/ directory for changes, and to run the
imagemin task if it does.
Try dropping a new image into images-src/ and you should see your terminal do it’s thing and the browser window reload.
Add clean task (grunt-contrib-clean)
Another useful task we could add at this stage is grunt-contrib-clean. The reason I’m introducing it at this point is because what I actually want to do is delete the images/ folder before I optimise images-src/. If you don’t do this your images/ folder could become full of files you’re no longer using -
grunt-contrib-imagemin doesn’t remove files from the destination directory if/when you remove them from the source directory.
Let’s go through the usual process. First install the module:
Now add the task loader to your Gruntfile:
Then add the following config:
This basically creates a subtask of
images that simply removes the images/ directory. You need to be a little careful with the clean task because you could accidentally delete files/folders you don’t mean to - it won’t give you any warning, it’ll just do it, so maybe do a test first?
Add the task to our
And finally to our
Go ahead and run
grunt watch again whilst keeping an eye on the images/ directory. It might happen very quickly, but you should see it disappear and then reappear again when the tasks are run. If you don’t see anything happen (it’s probably too fast for your computer to register anything has changed) check your terminal output and you should see a line saying the
clean:images task ran OK.
A bit more Git
Now we’ve added our theme directory and some Grunt tasks let’s commit them to Git.
If you run
git status on the command line you’ll probably see something like this:
There are actually a few things here we don’t want to commit to Git. We don’t want to track changes to .sass-cache/ and there’s no need to track node_modules/ either - the beauty of Node and npm is that everything we need to know is in package.json. If we want to set this project up on another computer we have only to clone the repo, run
npm install and all the modules we’ve specified will be installed automagically!
So, open your .gitignore file and add the following rules:
git status again and you see the above directories have disappeared and that the .gitignore file is now showing as modified. Add the changes:
You’ll notice I’ve specified each item in the
git add command. If you know a bit about Git you’ll know that you can simply say
git add . (the dot means ‘everything’) and all modified or new files will be added to the commit. However, this is kind of considered bad practice as you can’t be sure that you’re not going to add something you didn’t want to. Either way, it’s up to you how you do it (I’m lazy sometimes too).
Now you can commit your changes with a message and push them to the remote repo:
TIP: In reality you would probably want to do your commits on a much more granular level, after you’ve added each Grunt task for example. That way if you mess things up you can just roll-back to the last task/commit. You should probably also follow a branching model such as gitflow, rather than working directly on the master branch.
First, install the grunt-contrib-jshint module:
Then in your Gruntfile make sure the task is loaded:
Add the following task config (TIP: We might as well lint our Gruntfile too):
Add the task to our
And finally update the
watch task so
All that remains is to actually create a theme/skin/frontend/my-theme/default/js/ folder and a theme/skin/frontend/my-theme/default/js/scripts.js file to test our new
jshint task... Done that, I’ll wait while you do?
Now go ahead and run
grunt. Providing you’ve typed everything in correctly you should get the following message in your terminal:
grunt again. Your terminal should now go crazy and spit out a load of errors:
You can use this message to help bug-fix your code. I know the errors aren’t necessarily that clear (when are they ever?), but they’re a start…
I showed you earlier how you can use
So, following the usual process, install the module:
Load the task in your Gruntfile:
Add the following task config (TIP: setting
Note: You can’t use
skinDir in the first part of the
files config - that’s because we’re actually declaring a property name here, not just a string, so we can’t concatenate our
skinDir variable to it.
And finally add the task to the
Your terminal output should also look like this:
However, there’s one more thing you need to do - run
grunt again and see what happens. You should now see an error in your terminal:
This is because the
jshint to ignore any minified files. Change your
jshint task config to the following:
All we’ve done here is add a negation, by telling it to lint all the .js files in the js/ directory, but not (note the ‘!’) any that end in .min.js. Run
grunt again and you should no longer see the error.
Now we’ve got that working we can also add
uglify to our
As with the
jshint task we need to add some negation here by telling it not to watch for changes in the .min files. If you don’t do this your
watch task will just go round in an endless loop as it spots a change in scripts.min.js, re-lints and minifies scripts.js again, which triggers a change in scripts.min.js, and so on... Note that the syntax for negating .min files is slightly different here, but it’s doing the same thing as we had in our
jshint task config.
grunt watch, make a change to scripts.js again and watch the magic unfold before your eyes!
7. Watching for changes in XML and PHTML files
Start by creating the local.xml file:
Then in that file add the following code:
What we’re doing here is telling Magento to add an item to the head section of our page, specifically our scripts.min.js file in our skin’s js/ folder.
Now we’ve done that let’s tell our
watch task to look out for changes to our theme’s XML files. Update the
watch task config as follows:
All we’ve simply done is to tell it to watch for changes in any of the .xml files.
grunt watch and make a change to local.xml (just change scripts.min.js to scripts.js for example, that way you won’t actually ‘break’ the file) - you should see all the tasks run and the web page refresh. If you look closer at the terminal output you should this a line that looks like this:
And at the end of the output:
We can take this a step further though - let’s also tell the
watch task to keep an eye on our .phtml files. Change the line you just added to the
watch config so it now reads:
To test that it’s working let’s edit a file. Run
grunt watch and then open theme/app/design/frontend/my-theme/default/template/page/html/header.phtml and make a change in it - do something obvious, such as adding
When you save the file you should see all the Grunt tasks run as normal and your ‘Test’ heading appear on the web page as it refreshes.
And so we’ve reached the end of my ‘little’ tutorial. Don’t forget to keep committing your changes to Git.
And one last tip - let
grunt --verbose be your friend! If you’re having trouble getting anything to work, using the verbose flag will give you a lot more information about what’s happening, or not happening beneath the hood.
Here are the sections again in case you want to jump back up to any of them:
- Initial Magento setup
- Creating a (symlinked) theme
- Setting up version control (Git)
- Adding some theme files
- Setting up a Grunt environment
- Adding Grunt tasks
- Watching for changes in XML and PHTML files
So to sum it all up
What we’ve done here really only scratches the surface of what’s capable with Grunt. Lot’s of people are discovering the power of front-end automation and writing about it - A quick google and you’ll find loads of other resources to help you on your way.
Here are a few links you might find interesting if you want to take things further:
- Grunt for people who think things like Grunt are weird and hard - Was partly responsible for the inspiration behind this article and covers similar ground.
- Crash course in Node, Bower and Grunt - A great intro video that goes into a bit more depth about Node and introduces you to Bower.
- Grunt Tips and Tricks - If you want to become a Grunt pro there are some great tips in here.
- A Beginner’s Guide to Grunt - An article I wrote a while ago out of frustration due to the lack of easy-to-follow Grunt documentation.
- Front-end Process - Flat Builds and Automation - Another article I’ve written that outlines a whole automated front-end workflow, using Yeoman, Bower, Grunt, and the InuitCSS Sass framework.
- Grunt - Synchronised Testing Between Browsers/Devices - Another little article by yours truly on how to synchronise your testing between devices (for example, so you can watch your changes refresh on your desktop and mobile device at the same time).
I hope, if you got this far, that you enjoyed the article and found it useful - Good luck!