Next.js 15 import alias not working with turbopack
Link to the code that reproduces this issue
https://github.com/aelassas/next-turbopack
To Reproduce
Create a Next.js 15 project with an internal package package1:
| - my-app/
| - packages/package1/
Add the alias in tsconfig.json:
{
"compilerOptions": {
...
"baseUrl": "./",
"paths": {
"@/*": ["./src/*"],
":package1": ["../packages/package1"],
},
}
}
Import :package1 in a page or component:
import * as package1 from ':package1'
...
Run the dev server with turbopack.
Current vs. Expected behavior
When I run the dev server without turbopack, it works fine. But when I try with turbopack I always get this error:
Module not found: Can't resolve ':package1'
Import map: aliased to relative "../packages/package1" inside of [project]/
Provide environment information
Platform: win32
Arch: x64
Version: Windows 11 Pro
Available memory (MB): 32593
Available CPU cores: 12
Binaries:
Node: 20.17.0
npm: N/A
Yarn: N/A
pnpm: N/A
Relevant Packages:
next: 15.0.2-canary.7 // Latest available version is detected (15.0.2-canary.7).
eslint-config-next: N/A
react: 19.0.0-rc-1631855f-20241023
react-dom: 19.0.0-rc-1631855f-20241023
typescript: 5.3.3
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
Turbopack
Which stage(s) are affected? (Select all that apply)
next dev (local)
Additional context
No response
I got this issue when I used a alias outside of the baseUrl. Had to restructure and use the experimental.turbo.resolveAlias in my next.config.ts to use that external package. I think if you added ":package1": "../packages/package1" there it should work. You might have to mess with the aliased path a bit to get it working.
@dualdetail I have tried with the following config before submiting this issue but got the same error:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
turbo: {
resolveAlias: {
':package1': '../packages/package1',
},
},
},
}
export default nextConfig
I think it should work with tsconfig.json without having to tweak turbo config.
@dualdetail After playing with turbo config, I found a workaround:
const nextConfig = {
experimental: {
turbo: {
root: '..',
resolveAlias: {
':package1': '../packages/package1',
':package2': '../packages/package2',
},
},
},
};
export default nextConfig;
Setting root to .. fixed the issue. But as I said in my previous comment, it should work with tsconfig.json without having to tweak turbo config.
Anyway, thanks for your hint.
Yeah it's just a workaround. Glad you got something working in the meantime
Turbopack has stricter filesystem access than webpack by default to ensure that files outside of the project directory are not leaking into the compilation which causes the caches to be invalidated too often (i.e. when you deploy a part of the work would always have to be repeated).
We automatically detect the root by looking for a lockfile, in case of your reproduction there is no lockfile so it uses the default of the original project directory. Relevant code: https://github.com/vercel/next.js/blob/1ce3913ef13a22c03d795df7cc8eec2cf0a7ee37/packages/next/src/lib/find-root.ts#L6
We should add a clearer error for these resolving failures to highlight the reason it can't resolve is the path going out of the project root.
We automatically detect the root by looking for a lockfile, in case of your reproduction there is no lockfile so it uses the default of the original project directory. Relevant code:
https://github.com/vercel/next.js/blob/1ce3913ef13a22c03d795df7cc8eec2cf0a7ee37/packages/next/src/lib/find-root.ts#L6
We should add a clearer error for these resolving failures to highlight the reason it can't resolve is the path going out of the project root.
In my case, I started with a single project and later migrated to workspaces. Turns out I still had a bun.lockb in the directory of a module, so Turbopack thought that's where the project started, even though there was another bun.lockb in the parent directory.
Deleting the ./apps/frontend/bun.lockb fix it for me, didn't even need the resolveAlias. Thanks!
I also have Same issue when i using turbopack but i have more Alias so i directly imported from tsconfig.json also i havve imageloader issue on turbopack
// next.config.ts
import { jsonc } from 'jsonc';
let tsconfig: any = jsonc.parse(fs.readFileSync(path.resolve(__dirname, 'tsconfig.json'), 'utf-8'));
...
const nextConfig: NextConfig = {
...
experimental: {
turbo: {
resolveAlias: {
...Object.fromEntries(
Object.entries(tsconfig.compilerOptions.paths).map(([key, value]: any) => [
key.replace('/*', ''),
path.resolve(path.resolve(), value[0].replace('/*', ''))
])
)
},
// ! Turbo Pack Missing Loader
// rules: {
// // ? Image Loader
// 'image/*': {
// loader: 'remote',
// asset: "sdfsd",
// },
// }
}
},
...
}
From the point of view of a working webpack configuration, this turbopack behaviour represents a regression. Willing to accept that our webpack approach is wrong, but it seems incorrect to have to maintain two contradicting configs for the two sets of tooling.
In our monorepo, modules within the repository are resolved directly to their typescript source in the module's src/index.ts with no build step. In most tooling this is simply the --condition flag, or a customCondition config but we found explicit aliasing to absolute module path to work for next webpack.
However, attempting the same within the dev task with turbo enabled results in errors logged like...
https://nextjs.org/docs/messages/module-not-found
[...]
Import map: aliased to server relative '/Users/user/workspace/monorepo-root/modules/my-module/src' inside of [project]/servers/my-server
I think server relative means it settles to ./Users/user/workspace/monorepo-root/modules/my-module/src which is surely a contradiction that makes it impossible to migrate to turbopack tooling given an absolute path seemed to be needed by webpack.
Here's our (anonymised) source which works fine for webpack and chokes on turbopack.
const REPO_SCOPE = "@my-scope";
const repoModules = Object.keys({
...dependencies,
...devDependencies,
}).filter((packageName) => packageName.startsWith(REPO_SCOPE));
const repoAlias = Object.fromEntries(
repoModules.map((moduleName) => {
const shortName = moduleName.replace(`${REPO_SCOPE}/`, ""); // remove scope
const modulePath = path.resolve(`../../modules/${shortName}/src`); // derive path
return [moduleName, modulePath];
}),
);
/** EXPORT RESULTING CONFIG */
export default {
webpack: (config, _options) => {
config.resolve.alias = {
...config.resolve.alias,
...repoAlias,
};
return config;
},
experimental: {
turbo: {
resolveAlias: repoAlias,
},
},
} satisfies NextConfig;
This is a pretty frustrating experience. Turbopack was announced as stable and ready for dev back in October 2024. How can it be considered stable if it still doesn’t support tsconfig paths?
This is a pretty frustrating experience. Turbopack was announced as stable and ready for dev back in October 2024. How can it be considered stable if it still doesn’t support tsconfig paths?
its now working properly... also fixed my image loader issue (latest version nextjs 15.3.3)
its now working properly... also fixed my image loader issue (latest version nextjs 15.3.3)
I use 15.3.3 - it doesn't work for me and the error is unclear :(
can you send me config ?
@MeetBhingradiya I suspect there’s something in our tsconfig that’s breaking everything. I’ll debug it tomorrow and try to figure out what’s causing it. I created a fresh Next.js project, and I see that path aliases work there.
If it’s still not working after my investigation, I’ll create a small repo with a reproducible example so you can take a look. Thanks for your help :)
@MeetBhingradiya I suspect there’s something in our tsconfig that’s breaking everything. I’ll debug it tomorrow and try to figure out what’s causing it. I created a fresh Next.js project, and I see that path aliases work there.
If it’s still not working after my investigation, I’ll create a small repo with a reproducible example so you can take a look. Thanks for your help :)
You can lookout for my "portfolio" repo you get it there next config, how i handled alias in turbopack, beside that i use webpack more but still its working with turbopack. @arodik
I've fixed it. We had rootDir and baseUrl pointed to src directory. Something like this:
{
"compilerOptions": {
"baseUrl": "src",
"rootDir": "src",
"paths": {
"@demo/models/*": ["models/*"],
"@demo/core/*": ["core/*"],
}
},
"include": [
"src",
"next-env.d.ts"
],
}
I've simplified the config using the fresh nextjs project as a reference and now we have something like this and it works perfectly:
{
"compilerOptions": {
"paths": {
"@demo/*": ["./src/*"],
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
Hope this will help people with similar problems :)
@demo/
So we dont need to use
{
turbopack: {
resolveAlias: {
'@': './',
'@pages': './pages',
'@public': './public',
}
}}
?
@demo/
So we dont need to use
{ turbopack: { resolveAlias: { '@': './', '@pages': './pages', '@public': './public', } }}?
I think it depends on your luck and tsconfig setup 😅 You should try. This thing is still very confusing, probably needs some attention from nextjs team to make it better
Verified that the initial issue happens because of a missing lockfile at the repository root. tsconfig paths is supported as documented: https://nextjs.org/docs/app/getting-started/installation#set-up-absolute-imports-and-module-path-aliases
If you find any cases it doesn't work as expected please open a separate issue with a reproduction. I checked @arodik's previous post with the config that didn't work and that works on the latest version of Next.js.
Going to close this issue as the original report is correctly failing to compile as it can't determine the application root to be the monorepo root since it's not a real workspaces setup.
This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.