JavaScript Modules and how to effectively work with Export Import

Subscribe to my newsletter and never miss my upcoming articles

Introduction

No one would like to work with the code having one gigantic JavaScript file with many unrelated functions. Moreover, when you need to use few of the functions from that file, you end up loading all the others unnecessarily. AMD was a great way of achieving modularization in JavaScript. Node.js already supports the module system using CommonJS.

Javascript(ES6)'s inclusion of modules was a game changer. We are able to take advantage of the modules functionality native way. In this post, I would like to introduce you to the JavaScript module concept and in-depth understanding of using the keywords export and import effectively. All the code snippet used in this post are in my gitHub repo .

JavaScript Modules

Most simple way putting,

In JavaScript, one script is one module. A module is nothing but a JavaScript file.

Modules can be used to keep the code footprint for a related area smaller, concise and as an independent unit. Using modules we can create reusable functionalities that automatically bring down the code quantity.

Less Code means Less Bugs, isn't it?

modules.png

Basics of export and import

Modules can be loaded into each other by using the keywords, export and import.

  • export: Using the export keyword we make the module features available to other modules. These features are usually the functions, however it is not limited to it. We will be able to export variables, objects, classes etc from a module as well.
  • import: As the name suggests, the import keyword used to import the features from other modules.

Let us understand these with a simple example. Let us assume, we have a JavaScript file(a module) called, calc.js. It exports two feature functions called sum and sub. It is no brainier what these functions do, they do summation and subtraction respectively.

// calc.js

export const sum = (a, b) => {
    return a + b;
};

export const sub = (a,b) => {
    return a - b;
};

As you noticed, the export keyword is in-front of each of the functions. The module exports these two functions so that other modules can import and make use of it. Now lets see the import part. We will be importing these function to another module called, index.js.

// index.js

import { sum, sub } from './calc.js';

console.log('The Sum is', sum(2,3));
console.log('The Sub is', sub(5,3));

Note, we first import both the functions with their names(sum and sub) from the module(JavaScript file), calc.js.

Finally, we will be importing(or loading) the index.js file to a html file say, index.html

<html>

    <head>
        <title>js-modules</title>
    </head>

    <body>
        <script type="module" src="./src/index.js"></script>
        <h1>
            See the Debugger Console...
        </h1>
    </body>

</html>

Important to note that, we use the type called, module in the script tag while loading the index.js file. This is absolutely required. Specifying the type as module causes the code to be treated as a JavaScript module.

💥 Two very important points to note here:

  • This way of exporting a module feature(functions, variables, classes etc) called, Named Export. With Named Export, import needs curly braces. Named Export also forces upon the fact, the import must use the same exported name of the function, variable etc. For above example if we import sum as sum1 , we will get following error:

    error_1.png

  • While importing the functions, the related module name in the import statement must have the .js extension.

    import { sum, sub } from './calc.js';
    

    If you are familiar with modules from node.js or with reactjs, you can import the same as,

    import { sum, sub } from './calc';
    

    With Vanilla JavaScript, you will be getting this error and we hope it gets supported sooner.

    error_2.png

You can find the source code used in this section of the post from this branch .

Export Together and the Aliases

In the above example we have seen, how to export the functions individually. We may have the situations where we need to export multiple things from a module. We can do all of them together.

const sum = (a, b) => {
    return a + b;
};

const sub = (a,b) => {
    return a - b;
};

export { sum, sub };

As we see in the code above, we are not using the export keyword for each of the functions. We are just exporting them together in the last line. With this, we can just import the functions as we have done before.

Aliases - While importing a function from a module, we can chose to use an alias name instead. Consider the following example where we have used the alias called add for the imported function sum.

import { sum as add, sub } from './calc.js';

console.log('The Sum is', add(2,3));
console.log('The Sub is', sub(5,3));

💥 Point to note here, once the alias is used, you can not use the old name to call the module features like function, variable etc. It is going to throw error:

error_3.png

You can find the source code used in this section of the post from this branch .

Importing as a Namespace

At times, we may have a need of importing a large number of functions from a module. It would be tedious and too much of code to import them as comma separated as we have seen so far. we can short-hand this by importing them together with a Namespace. Namespace is nothing but a name of our choice. Let us take a look into this example:

import * as Calc from './calc.js';

console.log('The Sum is', Calc.sum(2,3));
console.log('The Sub is', Calc.sub(5,3));

As we see here, we are importing * which means all that are exported from, calc.js module. More important fact to point here is, we are importing the features by a name(Namespace) called Calc. As we did that, we can access the function using that Namespace.

You can find the source code used in this section of the post from this branch .

Default export and When not to use it

JavaScript modules provide a special keyword called default with export to specify only one thing to export from a file. However technically, we can export both Named Export and Default Export from a single file but, we shouldn't mix them. Use either Named or Default export.

// whoami.js

const sayMyName = () => {
    return 'Tapas';
};

export default sayMyName;

As we see the default keyword with export in above code. A default exported function can be imported two ways.

  • Using default as syntax
    import { default as sayMyName } from './whoami.js';
    
  • Without the curly braces ({ })
    import sayMyName from './whoami.js';
    

💥 Few points to consider,

  • Try not to mix the default export and named export together. Use default export when only one thing to export.
  • While importing a feature that is exported with 'default', it is not mandatory to use the same name. Here is an example of how the sayMyName function can be imported,

    import { default as name } from './whoami.js';
    

    or

    import name from './whoami.js';
    

You can find the source code used in this section of the post from this branch .

Combine exports

We can combine the multiple exports from various modules and do a combined export from a single file. This is also called, re-export or aggregating. Let us understand this with an example,

  • We first export sum and sub as before from calc.js module. Here we have opted for named export.

     // calc.js
    
     const sum = (a, b) => {
         return a + b;
      };
    
     const sub = (a,b) => {
         return a - b;
     };
    
     export { sum, sub };
    
  • Then we export a function called sayMyName from another module called, whoami.js. This time, we have used default export.

     // whoami.js
    
     const sayMyName = () => {
         return 'Tapas';
     };
    
     export default sayMyName;
    
  • Now we can combine the exports from both the modules into one module and export from there. Let us name the new module called, combine.js.

     // combine.js
    
     export * as Calc from './calc.js';
     export name from './whoami.js';
    

    It is important to note the syntax here. It is almost like import but we are actually re-exporting them. The advantage here is, we just need to import only one file to get access to all these functions. Let us see, how are we going to do that,

  • Import the functions

    
     import * as Combine from './combine.js';
    
     console.log('The Sum is', Combine.Calc.sum(2,3));
     console.log('The Sub is', Combine.Calc.sub(5,3));
    
     console.log('The name is', Combine.name());
    

You can find the source code used in this section of the post from this branch .

Wrapping Up

Native support of modules in JavaScript is an extremely useful feature. We should be using more of export and import in vanilla JS as we do with Node.js or any other library/frameworks like, angular, react etc. Point to note here, the import mechanisms we got to see here are called static import. There is another import mechanism called, dynamic import which I am going to cover in my next post.

With ES6, JavaScript modules are supported by most of the browsers on desktop and mobile devices.

import_export.png

Here is one online resource I would recommend for more in-depth understanding of modules:

That's all for now. Hope it was useful to you and please like and share. 🍻🍻🍻

Comments (1)

mahmoud badwy's photo

Big Thanks Bro