DEV Community

Cover image for Building a basic website with Parcel
Craig Holliday
Craig Holliday

Posted on

Building a basic website with Parcel

You want to build a tiny website with just HTML, CSS, and JavaScript. You don't want to import a ton of libraries or use a framework that performs ultra enhanced low latency rendering under the hood.
You want a website that has some styling, maybe makes a request to an API, and that you can build, manage, and deploy simply.
So let's build that.

Here's a video version of this post if you'd rather watch us build the website:

A basic website

First, let's set the foundation with technically the only 3 files you need to make up a website.

Let's pull up our command line and navigate to where we'd like to create our new project.

Then we'll create the directory:

mkdir awesome-site
cd awesome-site
Enter fullscreen mode Exit fullscreen mode

Now we can create three files:

touch index.html
touch main.css
touch main.js
Enter fullscreen mode Exit fullscreen mode

And let's fill in our files like so:

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Example</title>
    <link rel="stylesheet" type="text/css" href="main.css" />
  </head>
  <body>
    <h1>Hello from HTML!</h1>

    <script src="main.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

main.css

h1 {
  color: magenta;
}
Enter fullscreen mode Exit fullscreen mode

main.js

console.log('Hello from JavaScript!');
Enter fullscreen mode Exit fullscreen mode

Now if we open index.html we will see Hello from HTML! in magenta and that's it, we have a website.

At this point, we have the bare minimum, but we want to continue to add features to our website. To help with that we want to use other developer's libraries.

So, how can we import a library that we can use in our website?

Importing a library

There are plenty of ways you can import a library. You can directly download a JavaScript file and add it the same way we are using main.js, you can include the JavaScript file from a CDN in your HTML, and you can setup a package manager.

We're going to look at setting up a package manager called NPM (Node Package Manager) because this will automatically download the necessary files as well as help manage dependencies going forward.

Let's setup NPM in our repo

npm init -y
Enter fullscreen mode Exit fullscreen mode

Running this command we are creating a package.json file with default values.

Now we will install a package called moment.js a library that helps with date formatting.

npm install moment
Enter fullscreen mode Exit fullscreen mode

If we look at our package.json file now we will see that moment has been added to our dependencies

{
  "name": "awesome-site",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "moment": "^2.29.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

To use moment first we'll need to include the moment.min.js file using a script tag in our index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Example</title>
    <link rel="stylesheet" type="text/css" href="main.css" />
  </head>
  <body>
    <h1>Hello from HTML!</h1>

    <script src="node_modules/moment/min/moment.min.js"></script>
    <script src="main.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Notice that we are adding moment.min.js above where we include main.js. This way we will load moment before we use the library in main.js.

Lets start using moment by changing main.js

const now = moment().format('MMMM Do YYYY, h:mm:ss a');

console.log(now); // September 30th 2020, 8:20:12 pm <- The time I am actually writing this
Enter fullscreen mode Exit fullscreen mode

When you open index.html moment will be loaded and the current time will be logged in the format defined above.

But wait, is there more we can do?

Using a bundler

JavaScript does not provide a way to import code from one file to another. Right now when we want to import and use a library we have to include the JavaScript file from node_modules with an exact path to the entry point file for the library inside our HTML. When we include the library in our HTML the JavaScript file is loaded into our HTML and will be defined as a global variable for files loaded after to use.

Not only is this inefficient but we'll also have a bug if we don't add our script tag in our HTML or if we have our tags in the incorrect order.

So what's the alternative?

We're using NPM right now which is the package manager for node.js. Node.js implements CommonJS modules which allow JavaScript to import and export code across files.

This is what our previous example looks like using node.js modules, Instead of including the moment library in our HTML with an HTML script tag we can load the library in our main.js file:

const moment = require('moment');

const now = moment().format('MMMM Do YYYY, h:mm:ss a');

console.log(now);
Enter fullscreen mode Exit fullscreen mode

This looks great but if we try to use this right now we will get this error:

Uncaught ReferenceError: require is not defined

The browser does not have access to the file system which means loading files is tricky.

To fix this error and be able to access the file system we need a module bundler. A JavaScript module bundler is a tool that will create an output of your files that is browser compatible. A module bundler will find all the require statements and replace them with the context of each required file.

It's awesome but can be complicated. So let's use a tool that takes every complication out of the equation.

Enter Parcel.

Parcel

Parcel is a web application bundler that is going to handle a bunch of things for us that previously we would have to set up ourselves.

Parcel will bundle all our JS, CSS, HTML, file assets, etc into a smaller set of files we can use to run our code. During the bundling Parcel will also transform our files so we can use the require or even the import syntax.

Parcel has other features you should check out too

Let's install Parcel in our project

npm install parcel-bundler --save-dev
Enter fullscreen mode Exit fullscreen mode

This will add the parcel-builder module as a dev dependency which is a module that is only required during development.

Now we'll add two scripts to our package.json

{
  "name": "awesome-site",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "dev": "parcel index.html",
    "build": "parcel build index.html --public-url ./"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "moment": "^2.29.0"
  },
  "devDependencies": {
    "parcel-bundler": "^1.12.4"
  }
}
Enter fullscreen mode Exit fullscreen mode

The dev script we added uses the parcel command and an entry file. This will be used during development and starts a server for us to use with hot-reloading.

The build script uses parcel build which will build the website to a /dist folder which is where our site will be served from when we deploy it.

Bringing it all together

Now that we have Parcel set up we can use the require statement in our main.js file.

Our main.js file will look like this now:

const moment = require('moment');

// The newer import statement will also work
// import moment from 'moment'

const now = moment().format('MMMM Do YYYY, h:mm:ss a');

console.log(now);
Enter fullscreen mode Exit fullscreen mode

And we can exclude the moment script tag from our HTML:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Example</title>
    <link rel="stylesheet" type="text/css" href="main.css" />
  </head>
  <body>
    <h1>Hello from HTML!</h1>
    <script src="main.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

We can now run our dev script npm run dev and open http://localhost:1234 we will see our website and if we have the console open we will see the current time logged as well!

(Also try updating any of the HTML, CSS, or JS and you'll see that the website will reload automatically)

Wrapping up

Our small website is all set up and ready for us to host the code on our service of choice (GitHub, GitLab, Bitbucket, Etc) and to deploy the site to the world.

Take this starting point and use it as a testing ground for your next project. Some other interesting extensions to this project would be to add PostCSS, use Sass, as well as add various ways to deploy.

Resources

Parcel

NPM

Moment.js

More robust example code

Discussion (24)

Collapse
yoursunny profile image
Junxiao Shi

My main complaint about Parcel is the "regenerate runtime" error that pops up in the browser. Parcel doesn't use a configuration file, but this really means that configuration is scattered around in babelrc and browserslist.

Collapse
patarapolw profile image
Pacharapol Withayasakpunt

But you did realize the solution? Either really install and import regenerator-runtime; or set browserslist higher.

Collapse
yoursunny profile image
Junxiao Shi

Yes, I changed browserslist - but it's mentioned nowhere in Parcel documentation.

Thread Thread
patarapolw profile image
Pacharapol Withayasakpunt

It's a known bug (in GitHub issues), and very common too; but Parcel team seems to be too busy to fix it.

Collapse
leob profile image
leob • Edited on

Would be interesting if you could elaborate what's the absolute minimum required CSS for a "working" website, such as CSS resets, typography, color scheme, responsive/grid, viewport settings etc.

I see so many people advocating "don't use a CSS framework, roll your own" but I'd like to see the required minimum CSS for a real world website demoed and explained somewhere.

Collapse
xyn profile image
Mydrax

Rolling your own "css framework" would be feasible if the product is fairly small or you have the required human resources to produce a maintainable, easy to use and well-documented stylesheet.

There is absololutely no harm in using a css framework and I personally don't think its even a remotely good idea to "roll your own" unless the aforementioned conditions have been met.

Maintaining css can be pretty annoying after all.

Collapse
craigaholliday profile image
Craig Holliday Author

I agree with Mydrax that there is no reason not to use a good CSS framework nowadays.
For just minimal CSS for an MVP I'd use this bare minimum stylesheet:
github.com/andybrewer/mvp

Collapse
jonrandy profile image
Jon Randy

Why on earth would you use a bundler for a project of this size? Better still, just use native import features to pull in dependencies from a CDN like SkyPack (e.g. import moment from 'https://cdn.skypack.dev/moment'). If everyone loaded dependencies like this, the web could become much faster as many, many libs would be cached in browsers

Collapse
micahlt profile image
Micah Lindley

Nice post! I've used Parcel since 2019 when a friend introduced me, and it's my favorite bundler. This is an awesome tutorial, however I would add two things. 1) Technically, JavaScript does provide a way to import and export code (in recent ES6), it's just not well supported. 2) Vercel is also a great place to host and even build Parcel sites in the cloud. Love your writing style as well! Have a great day πŸ‘‹

Collapse
craigaholliday profile image
Craig Holliday Author

Great addition! Funny enough after I wrote this I took a look at the new ES6 way of importing and exporting code as well as deployed an app with Vercel!
Thanks!

Collapse
shamimahossai13 profile image
Shamima Hossain

Thanks a lot I was struggling with understand what and why a bundler is used in a project ..your post helped clear out some confusions. The project that i'm working on uses Webpack...do you think parcel works good for projects of medium to large codebase?

Collapse
craigaholliday profile image
Craig Holliday Author

Pacharapol is spot on.
Parcel is faster and has some configuration (that I have not looked into deeply) but I have not tried it for my larger projects.

Webpack is not going away and is still great to use for any project of medium to large size. Mainly I would stick with anything that is working and try Parcel when you have a chance to experiment without breaking anything.

Collapse
patarapolw profile image
Pacharapol Withayasakpunt • Edited on

Parcel is supposedly faster than Webpack, but may sacrifice configurations.

And esbuild is even faster, but much less features.

Collapse
insanenaman profile image
Naman Gupta

Wow thats great. Some days back, I also made this small starter kit for static websites using Parcel. You can read about it here - dev.to/insanenaman/starter-kit-for...

Collapse
craigaholliday profile image
Craig Holliday Author

That's great! I really like how your post was put together πŸ‘

Collapse
insanenaman profile image
Naman Gupta

Glad you liked it.

Collapse
yoursunny profile image
Junxiao Shi

moment is now "legacy" because it lacks tree shaking. Don't use it.

Collapse
craigaholliday profile image
Craig Holliday Author

Yep in the new example code I went ahead and used Day.js. Great newer alternative.

Collapse
xyn profile image
Mydrax

I've used parcel in a few side projects and I love the fact it takes little to nothing to get started!

Collapse
louislow profile image
Louis Low

Ahh... Looks pretty nice though. My favorite Static-Site-Generator by using Markdown is 11ty. I would give it a try. Thanks.

Collapse
codebubb profile image
James Bubb

Yes! Love Parcel. You can also use import/export keywords within your JavaScript source; Parcel will take care of these and transpile it down into something the browser can work with.

Collapse
craigaholliday profile image
Craig Holliday Author

Yea! So easy to use and love using the import syntax

Collapse
alfredosalzillo profile image
Alfredo Salzillo 🐺 • Edited on

JavaScript does not provide a way to import code from one file to another.

What about es6 static and dynamic import?

Collapse
craigaholliday profile image
Craig Holliday Author

This is true. I'd say I'm a bit behind on some of the newest features so I should make a post just about importing and exporting in ES6.