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
:
"baseUrl": "client"
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.
Figured, the error was a webpack or babel issue.
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.
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:
And many others. None of that worked.
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.
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. 😀
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.
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
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
Hopefully that helps.