Table of Contents
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.
-
Created
env
FileAdded 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.
-
Modify Babel Config
Added babel plugin module resolver with
yarn add -D babel-plugin-module-resolver
. Then modified mybabel.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
- Go to your VS Code settings (not the JSON file the GUI)
- Search for "typescript.preferences.importModuleSpecifier"
- 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
- Opening a TS file
- Entering your command palette
- Searching "TypeScript: Restart TS server"
Hopefully that helps.
Answered By – Bens Steves
Answer Checked By – David Goodson (BugsFixing Volunteer)