JavaScript Tools

The tools real developers use — package managers, bundlers, and how to make your code work everywhere.

When you are just starting out, you write JavaScript in a single file and open it in the browser. That works great for small things. But the moment you start building real projects, you will run into a bunch of questions.

How do you use someone else's code without copying and pasting it? How do you combine ten different files into one for the browser? What do you do when a browser does not support the latest JavaScript features? How do you make sure your code runs the same way on Chrome, Firefox, and Safari?

That is exactly what JavaScript tools are for. They are the things that sit around your code and help you manage dependencies, bundle files, convert modern code so older browsers can understand it, and fill in gaps where certain features are missing.

In this tutorial, we will walk through the most important ones — NPM, bundlers, Parcel, Babel, polyfills, and transpiling. None of this is magic. Once you understand what each tool actually does, it all starts to make a lot of sense.

Good to know: You do not need all of these tools for every project. Start with NPM, learn what it does, and pick up the others as you need them.

NPM stands for Node Package Manager. It is the world's largest software registry — essentially an app store for JavaScript code.

Developers around the world write useful pieces of code (called packages or libraries) and publish them to NPM. When you need that functionality in your project, you just install it instead of writing it yourself.

For example, need to format dates nicely? There is a package for that. Need to validate emails? There is a package for that too. Need to make HTTP requests? You guessed it — there is a package.

NPM comes bundled with Node.js. Once you install Node, you automatically have NPM too. To check if you have it:

bash

node --version
npm --version

To start a new project with NPM, you run npm init in your project folder. This creates a package.json file which acts like a project manifest — it keeps track of your project name, version, and all the packages you are using.

bash

mkdir my-project
cd my-project
npm init -y   // -y means accept all defaults, creates package.json quickly

To install a package, you use npm install. For example, to install a popular utility library called lodash:

bash

npm install lodash

After running this, two things happen:

  • A node_modules folder is created with the actual package code inside it
  • Your package.json is updated to list lodash as a dependency

Your package.json will look something like this:

json

{
  "name": "my-project",
  "version": "1.0.0",
  "dependencies": {
    "lodash": "^4.17.21"
  }
}

When someone else clones your project, they do not get the node_modules folder (it is usually in .gitignore). But all they need to do is run npm install and NPM reads the package.json and installs everything automatically.

Remember: Never manually edit the node_modules folder. Install and remove packages using NPM commands and let it manage that folder for you.

A package manager is a tool that helps you install, update, and remove packages in your project. NPM is one, but it is not the only one. You will also come across:

  • Yarn — created by Facebook as a faster alternative to NPM. Same idea, slightly different commands.
  • pnpm — a newer, very efficient package manager that saves disk space by sharing packages across projects.

All three do the same core job: they read package.json, download the right packages, and put them in node_modules. The choice between them usually comes down to team preference or project setup.

Here is a quick comparison of common commands across the three:

bash

// Install all dependencies
npm install
yarn install
pnpm install

// Add a new package
npm install react
yarn add react
pnpm add react

// Remove a package
npm uninstall react
yarn remove react
pnpm remove react

// Run a script defined in package.json
npm run build
yarn build
pnpm run build

One thing all package managers share is the concept of a lock file. This is a file that records the exact version of every package that was installed. NPM creates package-lock.json, Yarn creates yarn.lock, and pnpm creates pnpm-lock.yaml.

The lock file matters because it means if you share your project with a teammate, they will install the exact same versions of everything — not just "compatible" versions. This prevents the classic "it works on my machine" problem.

Tip: Always commit your lock file to version control. It is not optional — it keeps your team and your deployments in sync.

As your project grows, you start splitting your code into multiple files and modules. That is good practice. But browsers traditionally work best when they load fewer, smaller files. So how do you reconcile that?

That is where bundlers come in. A bundler takes all your JavaScript files, figures out how they are connected, and combines them into one (or a few) optimised files. It acts as a packing machine — you throw in a bunch of loose items and it packs them neatly into a box ready for shipping.

A bundler does a few key things:

  • Resolves imports — it follows all your import statements and pulls in the right code
  • Combines files — merges everything into one output file (or a few chunks)
  • Tree shaking — removes code you imported but never used
  • Minifies — shrinks the output file by removing whitespace and shortening variable names

The most popular bundlers in use today are:

  • Webpack — the most widely used, very powerful, but has a steeper learning curve due to lots of configuration
  • Parcel — zero-config bundler, works right out of the box, great for beginners and smaller projects
  • Vite — a modern, blazing-fast bundler becoming very popular especially with React, Vue, and Svelte projects
  • Rollup — often used for building libraries rather than full applications

Without a bundler, you would either have to manually manage the order in which scripts load, or use the browser's native ES module system. Bundlers make this automatic and more efficient.

Good to know: Modern frameworks like React (Create React App, Vite) and Vue come with a bundler pre-configured. You might be using one without even knowing it.

Parcel is a bundler with a big selling point: zero configuration. With most bundlers you need to set up a config file and tell it what to do. Parcel just figures it out.

You point Parcel at your HTML file and it automatically finds all the JavaScript, CSS, and other assets, bundles everything up, and spits out a production-ready version of your project. It even has a built-in development server with hot reloading — so the page automatically refreshes when you save a file.

Here is how easy it is to use Parcel:

bash

// Step 1: Install Parcel globally or as a dev dependency
npm install --save-dev parcel

// Step 2: Add scripts to package.json
// "scripts": {
//   "start": "parcel index.html",
//   "build": "parcel build index.html"
// }

// Step 3: Start the dev server
npm run start

// Step 4: Build for production
npm run build

That is genuinely it. Parcel reads your index.html, follows the <script> tags, and bundles everything. During development it serves a local URL (usually http://localhost:1234) and refreshes automatically on changes. For production it outputs optimised files in a dist folder.

Parcel also automatically runs Babel (more on that next) under the hood, so your modern JavaScript gets transpiled to work in older browsers without you having to do anything extra.

When to use Parcel: If you want to get up and running quickly without fussing over configuration, Parcel is a great starting point. For large-scale projects with very specific needs, Webpack or Vite might give you more control.

JavaScript keeps getting better. Every year new features are added to the language. But browsers take time to support them. Some users are on older browsers. Some are stuck on corporate machines that have not been updated in years.

So what do you do when you want to use modern JavaScript features, but you need your code to work on older browsers too?

You use Babel. Babel is a JavaScript compiler that converts your modern code into an older version of JavaScript that a wider range of browsers can understand. This process is called transpiling (more on that in a moment).

Here is a simple example. You write this modern code using an arrow function and a template literal:

javascript

// Modern JavaScript (ES6+)
const greet = (name) => `Hello, ${name}!`;
console.log(greet("Developer"));

Babel converts it to something like this, which older browsers can run:

javascript

// After Babel (ES5 compatible)
"use strict";
var greet = function greet(name) {
  return "Hello, " + name + "!";
};
console.log(greet("Developer"));

Same result, older syntax. You write the clean modern code, Babel handles the conversion behind the scenes.

Babel works through plugins and presets. A preset is just a bundle of plugins. The most common one you will see is @babel/preset-env, which tells Babel to compile down to whatever browser versions you need to support.

bash

npm install --save-dev @babel/core @babel/preset-env babel-loader

You then create a .babelrc file (or add a babel section in package.json) to configure it:

json

// .babelrc
{
  "presets": [
    ["@babel/preset-env", {
      "targets": "> 0.25%, not dead"
    }]
  ]
}

The targets setting tells Babel which browsers to support. > 0.25% means any browser used by more than 0.25% of users worldwide. Babel uses this to know how much it needs to convert.

Note: In most modern setups (Vite, Create React App, Parcel), Babel is already configured for you. You might never need to set it up manually. But it is good to understand what is happening behind the scenes.

Transpiling is the process of converting code from one version of a language to another. The word is a blend of "translate" and "compile".

It is different from regular compilation. When you compile code, you typically turn it into machine code or bytecode (a lower-level form). When you transpile, you stay at the same level of abstraction but target a different version — like going from modern JavaScript (ES2022) down to older JavaScript (ES5).

You have already seen how Babel transpiles arrow functions and template literals. Here are a few more examples of things Babel can convert:

javascript

// Modern: Destructuring
const { name, age } = person;

// After transpiling:
var name = person.name;
var age = person.age;

// -------------------------------------

// Modern: Default parameters
function greet(name = "World") {
  return "Hello, " + name;
}

// After transpiling:
function greet() {
  var name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "World";
  return "Hello, " + name;
}

// -------------------------------------

// Modern: Spread operator
const arr = [1, 2, 3];
const copy = [...arr];

// After transpiling:
var arr = [1, 2, 3];
var copy = arr.slice();

Transpiling is not just for JavaScript. TypeScript gets transpiled to JavaScript. JSX (the HTML-like syntax used in React) gets transpiled to regular JavaScript function calls. SCSS and Less get transpiled to plain CSS.

The idea is always the same: you write code in a more comfortable or powerful syntax, and a tool converts it to something the browser (or another runtime) can actually run.

Tip: You can try Babel's transpiling right in your browser at babeljs.io/repl. Paste in modern JavaScript on the left and see the ES5 output on the right. It is a great way to understand exactly what Babel does.

Babel can convert new syntax to older syntax. But some things are not syntax — they are brand new built-in features that simply do not exist in older browsers at all.

For example, Array.prototype.includes() was added in ES7 (2016). Older browsers do not have it. Babel cannot "transpile" it away because there is no older syntax equivalent. It is a missing feature.

That is where polyfills come in. A polyfill is a piece of code that adds a missing feature to an environment that does not have it. It basically fills in the gap.

A polyfill is a gap filler. If a new road has a pothole, you fill it so cars can drive smoothly. A polyfill fills in missing browser features so your code can run smoothly.

Here is what a polyfill for Array.prototype.includes looks like:

javascript

// Check if the method already exists - if not, add it
if (!Array.prototype.includes) {
  Array.prototype.includes = function(value) {
    for (var i = 0; i < this.length; i++) {
      if (this[i] === value) return true;
    }
    return false;
  };
}

// Now you can safely use .includes() even in old browsers
var roles = ["admin", "editor", "viewer"];
console.log(roles.includes("editor")); // true

You would not write these by hand. The most widely used polyfill library is core-js. When you configure Babel with @babel/preset-env, you can also tell it to automatically add polyfills for missing features:

json

// .babelrc - with polyfilling enabled
{
  "presets": [
    ["@babel/preset-env", {
      "targets": "> 0.25%, not dead",
      "useBuiltIns": "usage",
      "corejs": 3
    }]
  ]
}

With useBuiltIns: "usage", Babel scans your code, figures out which modern features you are actually using, and automatically includes only the polyfills you need. No bloat.

To summarise the difference:

  • Transpiling — converts new syntax into old syntax (Babel does this)
  • Polyfilling — adds missing features/methods that do not exist yet in the browser (core-js does this)
Real world: Modern build tools and frameworks handle both transpiling and polyfilling automatically. But knowing the difference helps you debug issues when something works in one browser and not another.
  • NPM is a package manager and registry — it lets you install and manage third-party JavaScript packages
  • A package.json file tracks your project's dependencies; a lock file pins the exact versions
  • Yarn and pnpm are alternative package managers that do the same job as NPM, often faster
  • Bundlers take multiple files and merge them into optimised output — popular ones include Webpack, Parcel, and Vite
  • Parcel is a zero-config bundler that works out of the box — great for getting started quickly
  • Babel is a JavaScript compiler (transpiler) that converts modern JavaScript to an older version for wider browser support
  • Transpiling converts new syntax to old syntax — same language, different version
  • Polyfills add missing browser features that cannot be transpiled — they fill in gaps
  • Most modern frameworks (React, Vue, Svelte) come with these tools pre-configured so you can focus on writing code

What's next? Now that you understand the tools developers use to ship JavaScript, check out Testing JavaScript to learn how to make sure your code actually works.

  • What is NPM and what problem does it solve?
  • What is the purpose of package.json?
  • What is a lock file and why should you commit it?
  • What is the difference between dependencies and devDependencies in package.json?
  • What does a bundler do and why do we need one?
  • What is the difference between Webpack and Parcel?
  • What is Babel and what is it used for?
  • What is the difference between transpiling and polyfilling?
  • Can Babel fix missing browser features like Promise or Array.includes?
  • What is core-js and how does it work with Babel?
  • What is tree shaking in the context of bundlers?