require files from a different part of the codebase. Too much of this can be a sign of a system that isn’t structured very well, but even in a well-architected, modular codebase, there will be dependencies between the modules.
So what do you do when you get tired of typing
import MyComponent from '../../../modules/MyModule'?
Part of it depends on the environment and toolset you’re using. Different tools have different options for addressing this problem. You also have to think about how other tools will deal with the solution, such as editors/IDEs, linters, etc.
The answers might be different for libraries than they are for applications. In this post, I’m focused mainly on applications.
There’s a very good summary and subsequent discussion about this issue for Node.js in a Gist by Bran van der Meer.
Here are few options I’ve considered or used.
There are a couple of ways of solving the problem with
NODE_PATH, and I’ve used that option the past. However, the Node.js module documentation says:
NODE_PATH is still supported, but is less necessary now that the Node.js ecosystem has settled on a convention for locating dependent modules. Sometimes deployments that rely on NODE_PATH show surprising behavior when people are unaware that NODE_PATH must be set. Sometimes a module’s dependencies change, causing a different version (or even a different module) to be loaded as the NODE_PATH is searched.
The Browserify Handbook also talks about this option:
You might see some places talk about using the $NODE_PATH environment variable or opts.paths to add directories for node and browserify to look in to find modules.
Unlike most other platforms, using a shell-style array of path directories with $NODE_PATH is not as favorable in node compared to making effective use of the node_modules directory.
This is because your application is more tightly coupled to a runtime environment configuration so there are more moving parts and your application will only work when your environment is setup correctly.
node and browserify both support but discourage the use of $NODE_PATH.
For those reasons, I’m not such a fan of this option any more.
Put Code in node_modules
The Browserify Handbook has some alternative suggestions, such as putting some of your modules under
node_modules, either directly or via symbolic links.
This seems like more hassle than it’s worth. You have to do interesting things with your
.gitignore to make it work, and also remember that
node_modules doesn’t just contain third-party dependencies.
Break Things Into Modules
If your application already has a modular structure, you could break it into separate npm packages. These could be private packages that you host in a private repository.
Nick Sellen does a great job talking about the various options for this approach.
When an application is under heavy development, it might not be clear where the module boundaries should be yet, so it would be premature to break it up. I prefer a solution that makes it easy to change my mind about where things belong and perform the subsequent refactoring.
If you’re using Webpack, you can use the
resolve.root setting, as described in an article by Grgur Grisogono.
In your webpack configuration file, add:
Using this setting, you can then do
import MyComponent from 'modules/MyModule' and webpack will find the file relative to your top-level
In our projects at work, this is our preferred solution and we have it configured in our react-boilerplate project.
My reason for researching this topic is that I ran into the same
import problem in a new React Native project I’m working on. React Native doesn’t use Webpack, so we can’t use its
resolve.root setting like we normally do.
React Native allows for a new trick that doesn’t seem to work in the Node.js environment. It turns out that you can place a very minimal
package.json file at the root of your source tree:
With that in place, you can do
import MyComponent from '@/modules/MyModule', where
@ is the name used in the
The React Native package resolver will walk up the directory tree, find your
package.json file with the name
@, and resolve the import relative to the
main entry in that file.
This solution is described more fully by Mike Grabowski.
I ran into a use of this technique in the React Native Katas project.
While this is an interesting technique, it feels like it’s based on an internal implementation detail that may not work in the future.
In my research, I also ran into the suggestion to use a Babel plugin for this. If you’re already using Babel, this is a viable option. And if you’re using React Native, you are using Babel.
I saw a number of references to
babel-plugin-module-alias. That plugin has since been renamed to babel-plugin-module-resolver.
To use the plugin, you need to add it to your Babel configuration file (either
.babelrc or nested inside your
Note that React Native has its own Babel configuration. When you add your own Babel configuration to use this plugin, React Native will use it instead of its own configuration, so you have to make sure your configuration has what React Native needs. Fortunately, there is babel-preset-react-native that takes care of the details.
Here is the
.babelrc file I’m using on my project:
While I haven’t tried this plugin in a non-React Native project, there’s no reason it shouldn’t work elsewhere as well.
The project README also provides advice about editor integration for Atom and IntelliJ/WebStorm.
I’ve outlined a number of ways I’ve found to solve the project-relative
As I mention above, my current approach is to use Webpack’s
resolve.root setting for any project using Webpack, and to use
babel-plugin-module-resolver for React Native projects.