Workflow for static frontend development (Gulp + ReactJs + ES6)
Getting into frontend development could be overwhelming.
I recently started learning more about frontend development. My intent is to build some static websites to try out things like IPFS.
I wanted to have a productive workflow that allows me to create project, build it, test it, run it and package it. Coming to the frontend world I realized things are really confusing as there are a lot of different tools.
In this post we are going to see how we can structure our project and use Gulp to do common tasks while developing. I am still learning so this post is just describing what I have learned and I plan to improve my understanding & workflow over time.
Goal of the workflow
My current aim with the workflow is to
- Create small JS libraries and use them in our web app.
- Develop in ES6 using Babel.
- Compiled JS files should have debug information so we can debug in ES6.
- Use a package manager to get JS packages e.g. reactjs.
- Have the capability of livereload so any changes to JS would automagically reflect in the browser.
These are the basic things I needed to get started.
Tools for the job
I would be creating the workflow with React in mind but you should easily be able to replace the react specific things to any other framework of your liking.
Npm is the package manager that comes with node. My aim is to develop not for node but instead create a static web app, so bower is the package manager specifically designed with frontend in mind. You might wonder why as one can use npm for frontend as well, a good answer I found regarding that was on the omniscient Stackoverflow.
We have to use a task runner which runs our various tasks of compiling ES6 to JS, bundling it and reloading browser on change. The major two task runners are Grunt & Gulp. The major difference between the two is that Grunt is more configuration based and Gulp is more code based.
I choose gulp because looking around I found that it was the fastest among the two and I liked its code based configuration. I also like the gulp model of chaining various tasks to create workflow.
As we discussed earlier that we will create small libraries that we will use in our main web app. The example application we are going to build is a webapp using React that allows you to add numbers. Internally adding number will use our
Following is the tree for the project structure
umbrella-project ├── calculationjs /* Calculationjs library */ │ ├── bower.json /* #Configuration for bower */ │ ├── gulpfile.js /* Configuration for gulp */ │ ├── index.js /* Our library code */ │ └── package.json /* #Npm configuraiton */ └── web-calculator /* Calculator web app */ ├── bower.json /* #Configuraiton for bower */ ├── gulpfile.js /* Configuration for gulp */ ├── package.json /* #Npm configuration */ └── src ├── index.html /* Our web page entry */ └── js └── app.js /* Our react app */ [Note] The ones starting by # are generated and maintained by tools like bower, npm
bower init in the directory where you want to create the bower package. Similarly to create
Go ahead and create the project structure.
calculationjs folder in terminal and run
npm install --save-dev gulp gulp-util browserify babelify vinyl-source-stream
This will install the require packages. Now lets see the how the
gulpfile.js looks like.
First we get all the dependencies that we just installed.
In gulp we have four main functions
gulp.taskis used to define a gulp task
gulp.srcis used to return a stream of files
gulp.destis used to write files to disk
gulp.watchis used to watch for any file changes and run the tasks that you list
.pipe operation is used to pipe output of previous function to the function that you pass to it. It is used to chain various transformations to create your workflow.
We define the build task where
browserify is passed in the entry point of your application. It will traverse the imports you have and figure out all the modules you have. We also specify
paths in browserify options to let it know which paths to look for modules if they are not specified as relative path. So if you install react then you can import it was
import React from 'react/react' where
'react/react' will resolve to
Then we use
transform and pass it
bundle is used to bundle all the modules into one file, the
source is used to convert the output of
bundle to representation that gulp works with i.e. vinyl files. At the end we just output the files to the destination using
watch task is used to watch any change in our JS files and then run the build so that you don't have to as you are developing.
We want to develop this library as we develop the web-calculator app so we need to link to this library. To do that run
bower link this will create a global reference in bower so that we can link to it.
Web calculator application
web-calculator folder in terminal and run
npm install --save-dev gulp gulp-util browserify babelify vinyl-source-stream watchify browser-sync
bower link calculationjs to link to that library and run
bower install react to install Reactjs.
In the gulpfile for web-calculator we have a new task
copy-static which copies static file (in our case index.html) to destination directory. We use
gulp.src to get the stream of the static files we have and then use
gulp.dest to place the files in our destination directory.
build task is exactly the same as we had in our calculationjs project. The
watch task is different. We use
wacthify package to watch files for change instead of
gulp.watch, on a file change recompiles all the browserify dependencies, making it slow. Watchify doesn't do that, it only recompiles the browserify bundle that changed.
In the watchify
update callback we specify what to do when a file changes, most of it is similar to what we have in
build task apart from the last part i.e.
browserSync.reload. We use Browser Sync to do livereload and also sync the input across browsers so that you can test it across browser without any extra effort.
You may wonder why we have the following after the
.bundle() .pipe(source(paths.OUT)) .pipe(gulp.dest(paths.DEST_BUILD));
It is because the watchify does not emit
update callback until we call
bundle at least once and consume its stream. And that part also compiles the files for us before it starts watching for any changes.
At the end we just start the server for Browser Sync. Now you should be able to make changes in your code and see the browser auto reload with new changes, even if you make changes to calculationjs lib it should work fine.
You should now have an idea of the basics of gulp and be able to create more tasks to your liking.
You can get the whole example project @github.