Back

Relative and absolute imports in NextJS

Relative and absolute imports in NextJS

Working on a project requires accessing files. Most of these files are deeply nested, which makes them confusing and difficult to manage, highlighting the necessity for a developer in any discipline to be familiar with file access. In this article, we will be focusing on accessing files on a Nextjs project.

As we all know, Nextjs is a framework built with the Nodejs (Javascript) programming language, meaning it follows the rules defined by Nodejs. In Nodejs, for one to import a file into a module, it specifies the path to that particular file. It can be either Absolute, e.g., c:/user/javascript/src/absolute_path or Relative, e.g., ../src/absolute_path. Let’s explain the two methods.

Relative imports

In a relative import, the path should be relative to where the module importing the file exists. In relative importation, the path usually starts with a dot and a backslash, e.g., ./

To understand the relative import better, let’s consider a file structure in a Nextjs project.

project root

    ├── src
    │    ├── components
    |    |     |___ todoItem.js
    │    |     |___ todoDesc.js
    │    |     |___ todoHeader.js
    |    |     |
    |    |__ pages
    |    |    |__ todo.js
    |    |    |

In relative imports, the dot can be either one ./ or two ../, with each pointing to a different level. Using the file structure, let’s make it clear.

If we are importing the todo.js file into the todoItem.js file, because the todo.js file exists in another folder outside the current folder, we must change the pointer from the current folder to the current folder’s parent to access it using the ../ method. If we use the ../ method, the pointer will move to the src folder, making it possible to access the pages folder and the todo file. The code looks like this.

    /*
    Before using the "../" method, the pointer exists in the current folder, which in our case, is the component folder.
    
    File structure
    |__ components
    |     |__ todoItem.js
    |     |__ todoDesc.js
    |     |__ todoHeader.js
    |     |  

    making it impossible for us to access the pages folder
    */

    import todo from "../pages/todo"

    /* After using the "../" method the pointer moves back to the parent folder of our current folder, which in our case is the src folder

    The New File structure becomes
    ├── src
    │    ├── components
    |    |     |___ todoItem.js
    │    |     |___ todoDesc.js
    │    |     |___ todoHeader.js
    |    |     |
    |    |__ pages
    |    |    |__ todo.js
    |    |    |

    By using the path "../pages/todo we can access the file
    */

What if we want to import a file inside our current folder? How do we access it? This is where the ./ comes into effect. The ./ starting path tells the pointer to look for the file inside the current folder. Using our file structure as an example, let’s say we want to import the todoHeader.js into the todoItem.js.

    /*
    Before using the "./" method, the pointer exists in the current folder, which in our case, is the component folder.
    
    File structure
    |__ components
    |     |__ todoItem.js
    |     |__ todoDesc.js
    |     |__ todoHeader.js
    |     |  
    */

    import todo from "./todoHeader"

    /* After using the "./" method, the pointer still exists in our current folder, which in our case is the component folder
    
    New file structure
    |__ components
    |     |__ todoItem.js
    |     |__ todoDesc.js
    |     |__ todoHeader.js
    |     |  

    By using the path "./todoHeader we can access the file
    */

Drawback of relative imports

Not all relative imports are amicable. In actuality, they might be rather perplexing! The level of the directory we are now in has to be carefully monitored, as we can see from the example above.

Furthermore, relative imports might make development difficult, particularly in large projects. If our application expands, we can end up with a more complicated path.

Imagine a scenario where a deeply nested file called by another file is being moved to another folder. What happens is with I call a development disaster because you have to change the path of all files that imported the file, which you might end up creating bugs for yourself and decreasing your production time.

Session Replay for Developers

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — an open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

NextJS absolute imports

A simple method of importing modules is through an absolute import. This import type designates a path that begins at the project’s root.

As in the case of relative imports, there will be no need to keep track of directory levels. We’ll have a better method to access files.

We can also use an alias that looks like so: ‘@components/todo.js’;

To use absolute imports in a Next.js project, we need to configure it.

Creating a jsconfig.json file

We can set a project’s base directory in the jsconfig.json file. Since 9.4, we may utilize this file to determine the root files and carry out the path mapping required for absolute imports using Next.js.

To do this, we create a jsconfig.json in the root of our project and add the following configuration:

{
    "compilerOptions": {
        "baseUrl": "."
    }
}

Note that we would have added the code to tsconfig.json if we used TypeScript.

The baseUrl key lets us specify the base directory to locate modules. Making the value of the baseUrl ”.” tells JavaScript or TypeScript to start searching for files in the root directory.

Note that we must restart the dev server if we make changes to the jsconfig.json or tsconfig.json files.

Configuring the baseUrl

Using our previous structure and adding jsconfig.json, our structure should be:

project root

    ├── src
    │    ├── components
    |    |     |___ todoItem.js
    │    |     |___ todoDesc.js
    │    |     |___ todoHeader.js
    |    |     |
    |    |__ pages
    |    |    |__ todo.js
    |    |    |
    |    |    |
    |    |    |
    |__ jsconfig.json
    |
    |

Instead of assigning ”.” as the value of baseUrl, we can assign a directory as its value.

{
  "compilerOptions": {
    "baseUrl": "src"
  }
}

The src directory must be located relative to the project root mentioned in the file. This will allow absolute imports from the src directory. eg

// Importing todo.js into todoItem.js using relative import
import todo from "../pages/todo"

// After configuring our project we can import it using absolute import
import todo from "pages/todo"

Configuring module aliases

Absolute import is much simpler to utilize when custom module aliases are created to match different directories rather than only the basic directories for larger projects with several layers of deeply nested directories containing files.

Consider the following structure:

project root

    ├── src
    │    ├── components
    |    |     |___ todoItem.js
    │    |     |___ todoDesc.js
    │    |     |___ todoHeader.js
    |    |     |
    |    |__ pages
    |    |    |__ todo.js
    |    |    |
    |    |    |
    |    |    |
    |__ jsconfig.json
    |
    |

To configure the module alias, we will introduce a new key known as paths. Paths is a key that accepts the alias of a path as its values. We will update the config file to include paths entries in light of the file mentioned above structure. The jsconfig.json file will seem as follows when beginning from the root:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@components/*": ["src/components/*"],
      "@pages/*": ["src/pages*"],
    }
  }
}

The paths object contains entries whose paths, are located relative to the baseUrl. In the above code, the entries are relative to the project root because the value of baseUrl is ”.“.

If we use src as the value of baseUrl, the path entries will appear as follows:

{
  "compilerOptions": {
    "baseUrl": "src",
    "paths": {
      "@components/*": ["components/*"],
      "@pages/*": ["pages/*"],
    }
  }
}

If you notice, when specifying the paths, src was omitted because the baseUrl is pointing to src already.

The new path to access files in the components folder will become:

// using absolute import
import todo from "pages/todo"

// Using module aliases
import todo from "@pages/todo"

// This is because all files in pages have been aliased to @pages

While configuring our projects to use absolute imports, we might encounter some problems. Below is the way we can troubleshoot them.

First, we must ensure that Next.js is at least version 9.4 on our system. Then, if we make changes to the jsconfig.json configuration file, we must restart the Next.js project.

Best use case

We can draw the following conclusions:

  • Relative imports should be used while working on a little project that doesn’t need much configuration in the case of absolute imports.

  • If you are working on a very large project which involves many folders that are deeply nested, it is advised to use absolute imports. This is because it protects your project from a future problem that comes from using a relative import.

Conclusion

As we’ve learned, relative imports don’t require any setting, unlike absolute imports. However, relative imports can occasionally be unclear and have a negative impact on the development experience. With absolute imports, we have a clearer and shorter method for importing modules in Next.js.

A TIP FROM THE EDITOR: We have published several articles on NextJS; don’t miss Building CRUD Apps With NextJs, Working With Databases In Next.Js Using Prisma, or A Complete Guide To Next.Js Plus MongoDB, for example.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs and track user frustrations. Get complete visibility into your frontend with OpenReplay, the most advanced open-source session replay tool for developers.

OpenReplay