· Jake Worth

Absolute imports with baseUrl


You may have seen imports like this in an app:

app.tsx
import {Page} from '../../views/pages/Page';

Those ../../ paths get worse as the tree grows, and they break when you move files. Absolute imports fix that by resolving from a fixed root (usually src/).

It’s easier to think of an absolute import in contrast to a relative import, which looks like this:

app.tsx
import {Page} from '../../views/pages/Page';

Translating this code into words:

“Import the named export Page, which can be found ‘up’ two directories and inside the views/pages directory.”

Here’s that same import with an absolute path:

app.tsx
import {Page} from 'views/pages/Page';

In words:

“Import the named export Page, which can be found in the views/pages directory.”

That directory is relative to… somewhere. Typically it’s the src/ directory, but we can’t say that with just this much information.

The difference between these two statements is subtle; just a few characters. It’s configurable on every kind of JavaScript and TypeScript project I’ve worked on. Here’s how to enable it on a TypeScript Create React App application.

tsconfig.json
{
"compilerOptions": {
"baseUrl": "./src"
}
}

But, it’s different in every framework, so search “absolute imports in <framework>” for the implementation in yours.

Absolute imports have some major selling points: here are a few of them.

First, absolute imports auto-complete well. If you can tab-complete an import statement, and you’ve ever imported Page before in any component, with absolute imports that will work every time. Relative imports require that both importing components are in the same “distance” away from Page.

Second, absolute imports let me move files around. When all imports are absolute, there’s less friction to moving a file, because every import works in any location.

Third, absolute imports scale well. A large React component is typically going to have a lot of imports:

app.tsx
import {AboutUs} from '../../../views/pages/AboutUs';
import {Blog} from '../../../views/pages/Blog';
import {Dashboard} from '../../../views/pages/Dashboard';
// ...and many more

What value are these dots and slashes providing? Remove them, and I think readability increases:

app.tsx
import {AboutUs} from 'views/pages/AboutUs';
import {Blog} from 'views/pages/Blog';
import {Dashboard} from 'views/pages/Dashboard';

Most importantly, absolute imports decouple my code from the directory structure. The location of Page relative to where it’s imported is repetitive, low-value information, so we don’t encode it.

I still use relative imports occasionally. One use case is an app with co-located stylesheets and tests. In a directory such as this:

$ ls src/components/
Component.tsx
Component.module.scss
Component.test.tsx

Relative imports are arguably a helpful convention for conveying familiarity:

Component.tsx
import {styles} from './Component.module.scss';
Component.test.tsx
import {Component} from './Component';

TypeScript documentation - compiler options baseUrl