Table of contents
1.
Introduction
2.
Tree Shaking
3.
Concepts
3.1.
1. Static vs Dynamic Imports
3.2.
2. Named vs Default Exports
3.3.
3. Live Code vs Dead Code
3.4.
4. Side Effects
4.
Imports
5.
Side Effects
6.
Mark a function call as side-effect-free
7.
Minify the Output
8.
Frequently Asked Questions
8.1.
Can tree shaking remove unused code from third-party libraries?
8.2.
Does tree shaking work with CommonJS modules?
8.3.
Can tree shaking cause problems if done incorrectly?
9.
Conclusion
Last Updated: Oct 25, 2024
Easy

Tree Shaking in Javascript

Author Gaurav Gandhi
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Tree shaking is an important concept in JavaScript that helps optimize code by removing unused parts. It's a way to make your code smaller & faster by getting rid of code that isn't actually being used when your code is run. Tree shaking looks at the imports & exports in your code to figure out what's being used & what can be safely removed. This method helps in making the final files smaller & more efficient, which is crucial for faster page load times & better performance. 

Tree Shaking in Javascript

In this article, we'll discuss the basics of tree shaking, key concepts to understand, how imports & exports work with tree shaking, handling side effects, marking functions as side-effect-free, & minifying the output. 

Tree Shaking

Tree shaking is a term used in JavaScript to describe the process of removing dead code. Dead code is code that is written but never actually used when the program runs. It's like having a bunch of tools in your toolbox that you never use—they just take up space and add weight without providing any benefit. 


In JavaScript, tree shaking is done by analyzing the static structure of your code (without running it). The "tree" refers to your code's dependency graph - the relationships between different files & modules in your project. "Shaking" refers to removing the dead branches from this tree - the unused code that can be safely deleted.


The goal of tree shaking is to end up with a smaller, more efficient JavaScript bundle that doesn't contain any unnecessary code. This is especially important for web apps, where the JavaScript bundle has to be downloaded by the user's browser. A smaller bundle means a faster download & a quicker start-up time for your app.


Module bundlers like Webpack, Rollup, and Parcel support tree shaking. These tools analyze your code's import and export statements to determine which parts are actually being used. They then remove the unused parts during the build process, resulting in an optimized bundle.

Concepts

To understand how tree shaking works, you need to know a few key concepts. These concepts relate to how JavaScript modules work and how they can be optimized.

1. Static vs Dynamic Imports

In JavaScript, there are two ways to import modules: static imports and dynamic imports. Static imports use the `import` statement and are fully declarative. They allow the bundler to analyze the dependencies at build time. Dynamic imports use the `import()` function and load modules at runtime. Because they happen at runtime, dynamic imports cannot be tree-shaken.

2. Named vs Default Exports

JavaScript modules can export values in two ways: named exports & default exports. Named exports allow you to export multiple values from a module, each with its own name. Default exports allow you to export a single value as the default for the module. Tree shaking works best with named exports because they allow for a more granular analysis of what's being used.

3. Live Code vs Dead Code

Live code is code that is actually used by your app at runtime. Dead code, on the other hand, is code that is never used. The goal of tree shaking is to identify & remove dead code so that only live code remains in your final bundle.

4. Side Effects

A side effect is any code that performs an action other than calculating a value. Examples include modifying a global variable, making an HTTP request, or logging to the console. Code with side effects cannot be safely removed by tree shaking even if its result is unused because removing it would change the program's behavior.

Imports

How you import modules in your JavaScript code has a big impact on tree shaking. As mentioned in the concepts section, static imports are better than dynamic imports for tree shaking.

With static imports, you use the `import` keyword at the top of your file to declare which modules you want to import. This allows the bundler to analyze the dependencies at build time. 

For example : 

import { myFunction } from './myModule';


In this example, we're importing the `myFunction` named export from the `myModule` module. If `myFunction` is the only thing we use from `myModule,` then the bundler can safely remove the rest of the code in `myModule` during tree shaking.


On the other hand, dynamic imports use the `import()` function to load modules at runtime. 


For example : 

import('./myModule').then((module) => {
  module.myFunction();
});

 

In this case, the bundler cannot analyze the dependency at build time because it doesn't know which module will be loaded at runtime. As a result, it cannot tree shake the unused parts of `myModule.`

 

Note: To maximize tree shaking, use static imports wherever possible. Only use dynamic imports when you truly need the flexibility of loading modules at runtime.
 

Here is the "Side Effects" section:

Side Effects

Side effects are important to consider when it comes to tree shaking. A side effect is any code that performs an action other than just calculating a value. A different type of side effects are : 
 

- Modifying a global variable
 

- Making an HTTP request
 

- Logging to the console
 

- Mutating an object passed as an argument

 

Tree shaking cannot safely remove Code with side effects, even if its result is unused. This is because removing the code would change the program's behavior.

For example: 

let globalVar = 0;


function sideEffectFunction() {
  globalVar = 1;
  console.log('This function has side effects');
}


function pureFunction(x) {
  return x * 2;
}


sideEffectFunction();
console.log(pureFunction(3)); // Unused result
You can also try this code with Online Javascript Compiler
Run Code


Output

This function has side effects
6

In this code, `sideEffectFunction` modifies the global variable `globalVar` & logs a message to the console. Even if the result of calling `sideEffectFunction` is never used, the bundler cannot remove this function because doing so would change the value of `globalVar` & prevent the message from being logged.


On the other hand, `pureFunction` is a pure function- it has no side effects, and its result depends only on its input. If the result of calling `pureFunction` is never used, the bundler can safely remove it.


Note: To allow for effective tree shaking, it's best to avoid side effects where possible and isolate them when necessary. Pure functions are easier for bundlers to analyze and optimize.

Mark a function call as side-effect-free

In some cases, you may have a function that the bundler thinks has side effects, but you know it actually doesn't. In these situations, you can mark the function call as side-effect-free to tell the bundler that it's safe to remove the call if the result is unused.

The way you mark a function call as side-effect-free depends on the bundler you're using. For webpack, you can use the `/*#__PURE__*/` comment before the function call. 

For example : 

function myPureFunction(x) {
  return x * 2;
}


/*#__PURE__*/myPureFunction(3); // Unused result
 

In this code, the `/*#__PURE__*/` comment tells Webpack that `myPureFunction` is a pure function and that the call can be safely removed if the result is unused.
 

For Rollup, you can use the `treeshake.annotations` option in your configuration file. This option lets you specify which functions should be treated as pure. 
 

For example : 

// rollup.config.js
export default {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  },
  treeshake: {
    annotations: true
  }
};



// src/main.js
/*@__PURE__*/myPureFunction(3); // Unused result


In this case, the `/*@__PURE__*/` comment in the code works with the `treeshake.annotations` option in the Rollup configuration to mark `myPureFunction` as pure.

Note: Marking functions as side-effect-free can help the bundler remove more dead code & create a smaller bundle. However, it's important to only do this for functions that you're sure have no side effects.

Minify the Output

After tree shaking has removed the dead code from your bundle, you can further reduce the bundle size by minifying the code. Minification is the process of removing unnecessary characters (like whitespace, comments, & long variable names) from your code without changing its functionality.

Minification works by:
 

1. Removing whitespace, comments, & unused code
 

2. Shortening variable & function names
 

3. Collapsing multiple statements into one
 

This is a simple example of code before & after minification:

// Before minification
function addNumbers(a, b) {
  // Add two numbers
  return a + b;
}


// After minification
function a(b,c){return b+c}


As you can see, the minified code is much shorter, but it still does the same thing as the original code.


Most bundlers have options for minifying your code as part of the build process. For example, in webpack you can use the `TerserPlugin` to minify your JavaScript. Let’s see what that might look like in your webpack config:

Javascript: 

const TerserPlugin = require('terser-webpack-plugin');


module.exports = {
  optimization: {
    minimize: true,
    minimizer: [new TerserPlugin()],
  },
};


In Rollup, you can use the `terser` plugin to achieve the same thing:

Javascript

import { terser } from 'rollup-plugin-terser';

export default {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  },
  plugins: [terser()]
};


By minifying your code after tree shaking, you can reduce your bundle size as much as possible, which leads to faster load times and better performance for your web app.

Frequently Asked Questions

Can tree shaking remove unused code from third-party libraries?

Yes, tree shaking can remove unused code from third-party libraries, but only if the library is written in a way that supports it (using ES6 modules & exporting functions/variables individually).

Does tree shaking work with CommonJS modules?

Tree shaking works best with ES6 modules, but some bundlers (like webpack) can perform a limited form of tree shaking with CommonJS modules using static analysis.

Can tree shaking cause problems if done incorrectly?

Yes, if you mark a function as side-effect-free but it actually has side effects, tree shaking might remove code that's necessary for your application to work correctly. It's important to be careful & only mark functions as side-effect-free when you're sure about it.

Conclusion

In this article, we've learned about tree shaking, a powerful technique for removing dead code from your JavaScript bundles. We've explained key concepts like static vs. dynamic imports, named vs. default exports, side effects, and minification. We've also seen how to use tools like Webpack and Rollup to implement tree shaking in your build process. 
You can also check out our other blogs on Code360.

Live masterclass