[SOLVED] Absolute Imports: React and Typescript

Issue

Background

I have a React app bootstrapped using create-react-app and typescript. As the application has grown, (goal) I would like to implement absolute imports. I am using VS Code (Visual Studio Code) and with very little configuration, I got TS and VS Code to recognize my absolute imports.

For TS, I took the following steps in my tsconfig.json:

  • Change the ‘baseUrl’ to ‘client’: "baseUrl": "client"
  • Added ‘client’ to my include key: "include": ["./**/*.ts", "./**/*.tsx", "client"]

For VS Code, I changed my User Settings: Typescript -> Preferences: Import Module Specifier -> non-relative

That worked great. All of my imports were using absolute imports, no errors. But, when I ran the app, I got an error: Error: Cannot find module "component" I expected to see my app like I did before the absolute imports.

What I Tried

Figured, the error was a webpack or babel issue.

  1. Created env File

    Added the following to an env file in the root of the app (same location as my package.json)

NODE_PATH=client/

That did not work. Same error: Error: Cannot find module "components". Also tried changing NODE_PATH to REACT_APP_NODE_PATH that did not work either.

  1. Modify Babel Config

    Added babel plugin module resolver with yarn add -D babel-plugin-module-resolver. Then modified my babel.config.js to:

module.exports = { 
  env {...}, 
  plugins: [
    [
      'module-resolver',
      {
        cwd: 'babelrc',
        extensions: ['.ts', '.tsx', '.js'],
        alias: {
          client: './client',
        },
      },
    ],
  ]
}

That returns the same error. (I am restarting the server after every change to my config files)

Resources Referenced
I used a lot of different articles to try to find clarity. Here are some:

  • Absolute Imports with Create React App by Kyle Truong (here)
  • Absolute Imports in Create React App by Michael Bednarz (here)
  • Configuring React Absolute Imports For TypeScript by Justin Noel (here)
  • Use absolute path in React components (StackOverflow)
  • How to import js modules (with absolute path) in my typescript file in React application?

And many others. None of that worked.

Project Structure

My project structure is a little “unconventional” or not my typical pattern which could be causing an issue.

└── root dir
    ├── assets
    │   └── client
    │       ├── assets
    │       ├── components
    │       ├── hooks
    │       └── ...
    │   └── babel.config.js
    │   └── .babelrc
    │   └── webpack.config.js
    │   └── package.json
    └── server files (no server dir) 

So client is like my src in a typical react app. assets is the “entry dir” for my server which is in the root dir.

Any help would be appreciated.

Solution

Found the answer. There is no need for the env file. Only need to modify the tsconfig.json and the webpack.config.js files.

TS Config

Added two keys: baseUrl and paths. Set the baseUrl to your src or client directory. The paths key will contain an object with whatever you want to reference in your absolute path.

{
  "compilerOptions": {
    ...
    "baseUrl": "./client",
    "paths": {
      "client/*": ["./client/*"]
    },
    ...
  },
  "include": ["./**/*.ts", "./**/*.tsx"]
}

Webpack Config

Added an alias for "src" (in my case, "client").

module.exports = (env, options) => ({
  ...
  resolve: {
    alias: {
      client: path.resolve(__dirname, 'client/') // added this
    },
    extensions: ['.ts', '.tsx', '.js', '.jsx', '.json']
  }
})

Usage
To import something now, I can replace the relative import with "client".
This:

import TableHeader from '../../../components/Table/TableHeader'

becomes this:

import TableHeader from 'client/components/Table/TableHeader'

If there is a better way to do this, post your solution. 😀

Update: 🥧 Day 2022

To do this with newer versions of tsc or create-react-app

npx tsc --init

or

npx create-react-app my-new-app --template typescript

you only need to modify the baseUrl property in your tsconfig.json. If you want the base directory to be your root directory you would set baseUrl to . or src if you want it to be your source directory.

Example

File tree

.
└── my-new-app/
    ├── node_modules
    ├── src/
    │   ├── hello/
    │   │   └── world/
    │   │       └── foo/
    │   │           ├── bar.tsx
    │   │           └── sibling.tsx
    │   ├── components
    │   ├── layouts
    │   └── utils
    ├── index.ts
    ├── tsconfig.json
    ├── .prettierrc.json
    ├── package.json
    ├── yarn.lock
    └── .gitignore
// baseUrl: "." root
import Bar from "src/hello/world/foo/bar";

// baseUrl: "src" dir
import Bar from "hello/world/foo/bar";

You can still specify paths if you would like however I find the baseUrl to be enough.

VS Code

If you are using VS Code and you do this and it does not work you may need to tinker with your typescript.preferences.importModuleSpecifier setting.

Updating "typescript.preferences.importModuleSpecifier" Setting

  1. Go to your VS Code settings (not the JSON file the GUI)
  2. Search for "typescript.preferences.importModuleSpecifier"
  3. Verify that you have "shortest" or "non-relative" selected

Shortest means it will use a "non-relative" import only if that is shorter than using a relative import.

Example: Shortest vs. Non Relative Imports

import Bar from "src/hello/world/foo/bar";
import Bar from "./foo/bar"; // <- this is shorter so it will use this even though it's relative

If that isn’t working you may need to restart your ts server. You can do that by

  1. Opening a TS file
  2. Entering your command palette
  3. Searching "TypeScript: Restart TS server"

Hopefully that helps.

Answered By – Bens Steves

Answer Checked By – David Goodson (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *