Table of contents
This post is your introduction to Parcel.js, the self-proclaimed “blazing fast, zero configuration web application bundler.” Here’s everything you need to know to get started with it:
Many front-end developers have become frustrated with some of the complexities involved with building web apps today. It’s pretty standard practice to include some kind of build or bundling tool in a front-end workflow, thus many developers have looked into using a front-end tool like webpack.
Webpack is the most popular bundler and it followed on the heels of Require.js, Rollup, and similar solutions. But the learning curve for a tool like webpack is steep. Getting started with webpack isn’t easy due to its complex configurations. As a result, in recent years another solution has emerged. This tool is not necessarily a front-runner, but an easier-to-digest alternative on the front-end module bundler landscape. Introducing Parcel.js.
Parcel.js is exactly what many beginnings to intermediate developers want: A simple, low-configuration bundler that you can get up and running with quickly.
I hope this Parcel tutorial will provide an easy-to-follow introduction to this tool. I’ll cover the basics and then I’ll show you how to use Parcel to bundle a simple web app that incorporates Babel and Sass.
Why use a Web App Bundler?
Here’s a basic rundown of the benefits of using such a tool:
- Your app will have fewer HTTP requests due to scripts or stylesheets being combined
- Scripts and stylesheets can be loaded on demand, further improving performance
- Scripts and stylesheets can be automatically minified to deliver fewer kilobytes to the user
- Bundling and minification work is done automatically by the tool, minimizing manual work
- Development files are organized modularly, making your code much easier to maintain and debug
As you can see, the benefits are many, and mostly related to performance and project maintenance. There certainly are lots of reasons to use a bundler, if this is something you haven’t yet considered.
With that out of the way, let’s get started with the basics for getting up and running with Parcel.js. I’ll slowly go over the features in this tutorial using some simple examples that you’ll easily be able to follow along with.
Installing Parcel.js
You can install Parcel.js in your terminal using Yarn or npm. For this tutorial, I’ll use npm. Here’s the command to install it globally so you can use it on any project:
npm install parcel-bundler -g
The -g
flag installs it globally. If you only want to install it for a single project and add it to your project’s devDependencies
in package.json
, you can run the same command inside the root folder of the project using the --save-dev
flag instead of -g
:
npm install parcel-bundler --save-dev
With the global install (which will be the most common use case), you can initiate any given project using the init
command. Use the terminal to navigate to the folder you want to use as the root of your application and run:
npm init -y
The -y
flag prevents npm from asking any questions, using the defaults for the setup. Assuming my project is called parcel-demo
, this creates a package.json file at the root that looks like this:
{
"name": "parcel-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Creating a File Entry Point
For a basic project setup, I’m going to use Parcel.js on an index.html file that points to a primary JavaScript file called index.js (as shown in the package.json file). This HTML file will serve as my Parcel entry point. My HTML file will have a script element that points to my JavaScript file, so it will look something like this:
<!doctype html>
<html>
<head>
<title>Parcel Demo</title>
</head>
<body>
<script src="./js/index.js"></script>
</body>
</html>
Once I have the correct HTML file and JavaScript file in place, I can start Parcel’s built-in development server by running the following in my terminal inside my project’s folder:
parcel index.html
This starts the server and tells it what file to use as the entry point. As a result, I get the following message in my terminal session:
Server running at http://localhost:1234
√ Built in 887ms.
I can now open http://localhost:1234/
in my browser to view what I’ve built so far. The live server uses live reload and something called hot module replacement. This will automatically update modules on a page without doing a full page refresh. This way I can see the progress of my build in faster increments, as I work.
Once I have Parcel.js running with its server active, any changes I make to a file will automatically rebuild my app each time the file is saved. To see this in action, I’m going to add a simple console log line in my script. This will trigger the following message in my terminal:
$ parcel index.html
Server running at http://localhost:1234
√ Built in 1.08s.
√ Built in 28ms.
Each “Built…” line represents one build, triggered by a change in content and save.
If I want to use my own server, rather than Parcel’s built-in development server, I can use the watch
command:
parcel watch index.html
I get the same result in my terminal session, with Parcel.js building my app and then waiting for changes:
$ parcel watch index.html
√ Built in 855ms.
The dist/ Folder
Once I’ve started Parcel.js either in watch mode or via the built-in server, if I look inside my project’s folder, I’ll see a folder and file structure like the following:
index.html
package.json
js/
└───── index.js
dist/
└───── index.html
└───── js.00a46daa.js
└───── js.00a46daa.js.map
Notice the dist
folder that gets created automatically. This is where my production files are; I don’t touch any of these files. Notice that my Parcel build has automatically converted my index.js file to one with a unique cache-friendly version (with a revisioned file name). It’s also added a source map file.
If I look in my index.html file inside the dist
folder, I’ll see the following:
<!doctype html>
<html>
<head>
<title>Parcel Demo</title>
</head>
<body>
<script src="/js.00a46daa.js"></script>
</body>
</html>
Notice the dist
version of my index.html file points correctly and conveniently to the dist
version of my .js file.
If my website includes multiple files that point to the same scripts (for example, about.html, contact.html, etc.), I can use the following command:
parcel index.html about.html contact.html
This tells Parcel that I want to use multiple entry points to build from. I can also use the following command to tell Parcel.js to use all my HTML files as entry points:
parcel *.html
Using Parcel.js with Babel
Parcel.js has built-in support for different code transpilers, including Babel, the popular tool for converting modern next-generation JavaScript to equivalent code that can be understood by all browsers. Because Babel is built into Parcel.js, you don’t need a special plug-in to use it, it just works. Let’s look at an example.
I’m going to add the following code to my index.js file:
function getInfo (name, year = 2022, color = 'black') {
console.log(name, year, color);
}
getInfo('Ferrari', 1987, 'Red');
getInfo('Rolls Royce', 2018);
getInfo('Mercedes Benz');
This code uses an ES6 feature called default parameters, which you can see specifically in the function head. Older browsers don’t support default parameters for functions. To make sure the code doesn’t throw an error, I need Babel to transpile the code into equivalent code that works in all browsers. Once I’ve saved my index.js file, Parcel.js will rebuild my app and produce the following in place of the ES6 code I wrote:
function getInfo(name) {
var year = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2022;
var color = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'black';
console.log(name, year, color);
}
getInfo('Ferrari', 1987, 'Red');
getInfo('Rolls Royce', 2018);
getInfo('Mercedes Benz');
You can use Babel’s online repo to test a variation of this out.
And notice the most important factor here: I didn’t do anything to install or configure Babel – it just works out-of-the-box as part of Parcel’s default setup! Of course, you might want to add some config options to alter Babel to do what you want. If that’s the case, you can add a .babelrc file in your project’s root folder, with your configuration preferences included. You can read more about using a .babelrc file on the Parcel.js website.
Take a look at my GitHub Repo of a Formula 1 Driver Standings created with Pug, SCSS, and Babel.
Using Parcel.js with Sass
Similar to Babel, Parcel will also by default automatically compile my SCSS (Sass) files to valid CSS. To demonstrate this, I’m going to add a folder called “css” to the root of my example project. In that folder I’ll add a style.scss file with the following Sass code:
body {
color: black;
.module {
color: red;
}
}
I’m using Sass’s selector nesting feature. I’ll add the following line to the <head>
section of my HTML files:
<link rel="stylesheet" href="./css/style.scss">
Once all my files are saved, Parcel will produce a fresh build in my dist/
folder with a compiled CSS file that has converted my SCSS to the following:
body {
color: black;
}
body .module {
color: red;
}
As long as I have Sass installed on my system globally, Parcel will do this automatically, with no configuration needed. If I don’t have a global install of Sass, no problem. I can just write my SCSS, then start Parcel and Parcel will install Sass automatically as a project-specific dependency. Very convenient! And again this is especially useful because it doesn’t require any configuration.
And just like with Babel, I can choose to go with whatever default configuration options come with Sass. Or I can create a .sassrc
file in my project’s root and add my own configurations.
Bundling Multiple Scripts with Parcel
So far, the examples I’ve shown you are just to get you up and running with Parcel, so you can get a basic idea of how it functions. As mentioned earlier, the power of any bundler is the ability to automatically combine multiple files into a single file. This helps with reducing HTTP requests and improving the speed of your website or app.
Everything I’ve demonstrated so far can be done using multiple scripts that are pulled in using ES6’s module feature. This allows me to create and import scripts in a modular fashion. This keeps my code easy to maintain while still only serving a single bundled script in production.
To demonstrate this, I’m going to add a separate file called module.js that will contain the following JavaScript:
export let color = 'green';
export function add(n1, n2) {
return n1 + n2;
}
This is just some basic code to export two objects: A variable and a function. In my index.js file I’m going to import these resources with the following code:
import { color, add } from '../js/module.js';
console.log(color); // "green"
console.log(add(20, 40)); // 60
Again, this is just basic ES6 module syntax. I won’t go into the details of how this works here. The beautiful part about this is the fact that I did not need to specify in my HTML file that I was using a module. Normally my script tag will look like this, with the type
attribute set to module
:
<script src="./js/index.js" type="module"></script>
But this isn’t needed. Parcel recognizes the module being imported and bundles up my two scripts into a single performance-friendly resource. This happens without any special configuration or plugins. And just like the earlier examples, the code is transpiled using Babel into ES5-equivalent code that will give me the most browser support.
Code Splitting with Parcel.js
Yet another feature in Parcel that works without any configuration is code splitting. In some cases, I’ll want all my modules loaded on all my pages. But in other cases, I might only want to load certain modules on certain pages, in certain contexts. This is what code splitting allows me to do.
Earlier I mentioned that my example project includes three pages: index.html, about.html, and contact.html. Let’s say I want to run the same JavaScript bundle on all three pages, but on the about.html page, I have a button that triggers something specific. But I only want that code to load when that button is pressed.
Here’s how that code might look using the code-splitting feature:
if (document.querySelector('.about')) {
document.querySelector('.about').addEventListener('click', ()=> {
import('../js/about.js').then(
document.body.innerHTML += 'About Page Updated';
);
});
}
Notice This is incorporating a new JavaScript feature, dynamic imports using the import()
function. This allows me to dynamically load the code I want in a specific instance. In this case, I’m doing it when a button is pressed on the about page. The import()
feature returns a promise, so I can do whatever I want inside the .then()
clause, which triggers once the imported script is loaded. The about.js script is loaded on demand and this code will be transpired by Babel to cross-browser ES5, to ensure it works everywhere. When my bundle gets created, the about.js portion gets put in its own file inside the dist/
folder, to enable this file to be loaded on demand.
Like other features I’ve discussed, the import()
feature works in Parcel without any configuration.
Production Builds with Parcel.js
Up until now, I’ve been producing all my Parcel builds on the fly using the built-in server that comes with Parcel and that includes live reload. Each time I save my project, my bundle is built. But the code was always bundled for ongoing development. This way I can view or inspect the source as needed to do some debugging.
Once my project is complete and ready to be pushed to a live server, I can stop Parcel from watching my project. CTRL-C in the terminal does this on many platforms. Then I’ll run the following command to tell Parcel to produce one final build:
parcel build index.html about.html contact.html
In this case, I’m building from all three of my entry files. Once this is done, Parcel is no longer waiting for changes; the final bundle is built and that’s it. In addition to the build being finalized, my code is prepared for production by Parcel. The HTML, CSS, and JavaScript are all minified to produce the smallest possible files for optimized performance.
Any Drawbacks to using Parcel?
The parcel should definitely be the go-to choice for those who have little or no experience with building tools. But in my own research, I’ve found a few things I can mention that should be improved in the future.
First of all, you’ll notice that Parcel always places your bundled scripts and stylesheets in the same directory as the entry point HTML files. This happens even if you have your CSS and JavaScript files in separate folders. Since these are production files, it might not matter much. But it’s something to keep in mind. The good news is this seems to have been corrected in the upcoming Parcel version 2 (still in Alpha). So there should be a way to override this behavior when that version of Parcel is stable (the current stable version is 1.12.4).
Another drawback is that I found the Parcel documentation is pretty minimal when it comes to information on customizing the configuration options for the different features. It’s true that one of Parcel’s huge benefits is how well it works out of the box. But I think some more extensive documentation on customizing it would be useful. For example, how do I disable HTML minification on my builds, while maintaining CSS and JavaScript minification? I don’t see a description of that in the docs. When I looked through the closed issues on GitHub, there is a workaround (though it’s not very convenient).
Summary
One last thing I’ll mention is a tool called Create App, which you can use to automatically produce configuration files for Parcel. It lets you select your JavaScript library, transpiler, linter, and so forth, and these will all be added to your package.json file, ready to be initialized.
That’s it for this Parcel tutorial for beginners. I hope you’ll be able to use this information to get up and running quickly with Parcel and use it on future projects. And if you’re new to app bundling, or have never tried it on any project, maybe this introduction will inspire you to give Parcel a try.
Conclusion
If you liked this blog post, follow me on Twitter & LinkedIn where I post daily about Tech related things! If you enjoyed this article & would like to leave a tip — click here