Is Import or Require better for JavaScript Modules?

Import and Require are both valid ways for including JavaScript Modules, but which is best? Find out what the differences are and which to use in your project.

Dan Spratling

January 5, 2021

6 min read

I've been creating websites for years, but have never thought too hard about how i actually include my libraries. Is there even a right option? Which approach should you use?

The different ways to include

There are a few different ways which you can include files in javascript. You probably use whichever approach you're most familiar with, or whichever the documentation tells you to use.

We can require modules which includes them in our application

const path = require('path')

but we can also import them

import path from 'path'

After importing via either method, we can use the library in our app which works the same way for both include methods.

//join is a function offered by the path library
path.join(__dirname, filename)

In terms of usage, there isn't much difference here.

How including can affect performance

Above, we are including the entire path library just to use a single function, but path has a lot more functionality than just that one function and including the entire library to use a single function is inefficient.

We can include just the functions we need with both methods

const join = require('path').join 
import { join } from 'path'

Both of these again will work in the same way as before, but now we can drop the library. When using either method there's no size difference (both will include just the join method from the library as well as anything join may depend on).

// now we don't need to specify path.join as join is included directly
join(__dirname, filename)

One thing to consider is if you have a library with a lot of functions you need to use, the include can be cleaner using the import approach.

// each require is indivudial
const join = require('path').join
const parse = require('path').parse
const sep = require('path').sep
// but we can group includes when using import
import { join, parse, sep } from 'path'

This may be something which impacts your project, especially if it's extremely large but for most projects this will be a fairly minor difference, and there's no file size difference between the two.

It's worth noting that the import above will include the entire library instead of just join if the library doesn't take advantage of modules, making it much less efficient. Be wary of this. If you run into this issue but still want to use import you can instead include only the module using import join from 'path/join'

Compatibility

This is where we start to see some real differences. import was only introduced in es6 (2015), with require being the main way to include packages before that.

This means that if you want to ensure compatibility with older browsers then using require is a safer choice, though most modern browsers support import and you can transpile back to older versions with babel too.

If you're writing in node.js rather than for the browser, then support for import has only been included much more recently, with official support for import only coming through in node 14 (2020), and with experimental support in node 12 (2019).

Futureproofing

As you can imagine, with import being the more modern standard it is built to allow more functionality for your app.

Asynchronous importing

import allows files to be read asynchronously, which can help improve performance when compiling and reading the files. You probably won't notice significant improvements in a standard app, but if your app includes a lot of external files, or the files you use are on a significantly slow connection, this may help.

Dynamic importing

import also allows files to be imported dynamically, so instead of including everything you need when the file is first read which may cause a bloated first load it will only include libraries when you need them. For an application which has a lot of functionality behind the scenes like many modern SaaS products, delaying the import of specific functionality until it's needed can have a serious performance benefit for the user.

Tree shaking

Another benefit of import is tree shaking. Tree shaking is a term for pruning modules which aren't being used in your app, ensuring that only the code you need is included. While included methods will often rely on other methods under the hood, you likely won't need everything. Using import allows anything which is completely unused to be shaken out so you're only left with useful code.

Of course, all of these approaches will only work if you don't transpile import to act like require so you'll have to give up compatibility on older browsers to gain these improvements.

Conclusion

There isn't really a correct way of including files, but the way you should use will depend on what you're trying to achieve.

require is better if you need to ensure compatibility with older browsers, especially if you aren't sure what your browser targets are.

include is better for most other scenarios, especially if you're trying to heavily optimize your app.

Regardless of which approach you choose, consistency is important as it helps other developers understand what you're aiming to achieve while avoiding

If you're just starting out, and you're not sure which to pick, I would suggest using include. It's the more modern standard, which means you won't be sacrificing anything by using it but if your app needs to support older standards it's fairly easy to convert import > require using a transpiler like babel, which means this is probably the way to go for most users. Just beware of including the entire library like mentioned above when doing this.

Here's what I recommend as the most flexible approach that avoids performance issues regardless of your config.

//for external modules
import { Link } from 'next/link'

//for your internal modules
import Hero from '../../components/Hero.js
import ArticleFeed from '../../components/ArticleFeed.js

Tags:

Development

Performance