storybook: Storybook is not able to resolve path with Next js absolute import

Describe the bug Storybook is not able to resolve path with Next js absolute import

To Reproduce Steps to reproduce the behavior: yarn add storybook Added .storybook folder Add jsconfig for absolute import

Expected behavior It should build correctly.

const path = require('path');
const resolve = (dir) => path.resolve(__dirname, dir);

module.exports = ({ config }) => {
  config.resolve.alias = {
    '@utils/*': resolve('../src/utils/*'),
   ....
  };

  return config;
};

Error


ERROR in ./src/components/Base/Button/Button.jsx
Module not found: Error: Can't resolve '@utils/boxShadowGenerator' in '/Users/rahulshrivastava/projects/unacademy-web-learning/src/components/Base/Button'
 @ ./src/components/Base/Button/Button.jsx 9:0-59 107:35-53 122:9-27
 @ ./src/stories/button.stories.js
 @ ./src/stories sync \.stories\.js$
 @ ./.storybook/config.js
 @ multi ./node_modules/@storybook/core/dist/server/common/polyfills.js ./node_modules/@storybook/core/dist/server/preview/globals.js ./.storybook/config.js (webpack)-hot-middleware/client.js?reload=true&quiet=true

Code snippets I am using jsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es2016",
    "jsx": "react",
    "baseUrl": "./src/",
    "paths": {
      "@utils/*": ["utils/*"],
    ....
    }
  },
  "exclude": ["node_modules", "dist"]
}

System:

 npx -p @storybook/cli@next sb info

Environment Info:
(node:1828) UnhandledPromiseRejectionWarning: TypeError: e.filter is not a function
    at /Users/rahulshrivastava/.npm/_npx/1031/lib/node_modules/@storybook/cli/node_modules/envinfo/dist/envinfo.js:1:73205
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async Promise.all (index 6)
(node:1828) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:1828) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 20
  • Comments: 41 (16 by maintainers)

Most upvoted comments

Its because thats not an absolute import from root, your path is using an alias. The following code allows you to import from src using “src/components/button/Button”:

config.resolve.modules = [
      path.resolve(__dirname, ".."),
      "node_modules",
    ]

But it does not tell webpack anything about the “@/Components” syntax. You would need to pass an alias configuration like this:

config.resolve.alias = {
      ...config.resolve.alias,
      "@/Components": path.resolve(__dirname, "../src/components")
    };

I havent gotten everything working with Next yet but this resolved the absolute import issues (in .storybook/webpack.config.js):

const path = require('path');

module.exports = ({config}) => {
  config.resolve.modules = [
    path.resolve(__dirname, ".."),
    "node_modules",
  ]

  return config
}

For using ./src, you can use path.resolve(__dirname, “…”, “src”),

You can still add a webpack.config.js to your .storybook directory. But with main.js, you define it as webpackFinal:

const path = require('path');

module.exports = {
  stories: ["../stories/*.stories.@(js|jsx)"],
  addons: [
    "@storybook/addon-actions",
    "@storybook/addon-knobs",
    "@storybook/addon-links",
  ],
  presets: ["@storybook/preset-scss"],
  webpackFinal: async (config, { configType }) => {
    config.resolve.modules = [
      path.resolve(__dirname, ".."),
      "node_modules",
    ]

    return config;
  }
};

Its because thats not an absolute import from root, your path is using an alias. The following code allows you to import from src using “src/components/button/Button”:

config.resolve.modules = [
      path.resolve(__dirname, ".."),
      "node_modules",
    ]

But it does not tell webpack anything about the “@/Components” syntax. You would need to pass an alias configuration like this:

config.resolve.alias = {
      ...config.resolve.alias,
      "@/Components": path.resolve(__dirname, "../src/components")
    };

thank you man you saved my day

that’s how it worked for next.js

const path = require('path');
module.exports = {
  stories: [
    '../desktop/**/*.stories.@(ts|tsx)',
    '../mobile/**/*.stories.@(ts|tsx)',
  ],
  addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
  webpackFinal: async (config, { configType }) => {
    config.resolve.modules = [path.resolve(__dirname, '..'), 'node_modules'];
    config.resolve.alias = {
      ...config.resolve.alias,
      '@': path.resolve(__dirname, '../'),
    };
    return config;
  },
};

Great! It worked perfectly, thanks a lot man. I wrote a commit implementing your fix: Absolute path with Storybook problem solved

+1 Same problem here. NextJS resolves but SB don’t.

jsconfig.json

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

Basically the underlying issue, is that you need to reuse the same Webpack config in both Next and Storybook, and make sure your Webpack config is robust enough to be reused, eg using __dirname

For example I use this in Vulcan:

const withMagicImports = (config = {}) => {
  if (!config.resolve) config.resolve = {};
  config.resolve.alias = {
    ...config.resolve.alias,
    "~": path.join(__dirname, "../../../", "src"),
  };
  config.resolve.modules = [
    ...(config.resolve.modules || []),
    path.join(__dirname, "../../../", "packages"),
  ];
  return config;
};

I use that both in next.config.js and main.js to extend the Webpack config. It is robust to path changes because I use __dirname.

Maybe I can add a section in https://storybook.js.org/docs/react/configure/webpack for this advanced usage with a code sample if that’s a pretty common issue?

I love you man, I spent like about 40 hours trying to solve but I never could. Thanks ❤️

Its because thats not an absolute import from root, your path is using an alias. The following code allows you to import from src using “src/components/button/Button”:

config.resolve.modules = [
      path.resolve(__dirname, ".."),
      "node_modules",
    ]

But it does not tell webpack anything about the “@/Components” syntax. You would need to pass an alias configuration like this:

config.resolve.alias = {
      ...config.resolve.alias,
      "@/Components": path.resolve(__dirname, "../src/components")
    };

Hi everyone! Seems like there hasn’t been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don’t have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

Hi, I am going to close this for now, as there are 2 main alternatives:

  • just set Storybook up to reproduce Next.js magic, there are multiple comments demoing how to do so. This is of course not very satisfactory, but it works for the moment.

  • in the meantime we will keep working on @next/plugin-storybook to make this a one-liner, but this takes time as it is not so easy to export Next.js buildtime magic to 3rd parties at the moment. It technically works, but it’s not very stable. I’ll demo this package usage here: https://github.com/lbke/next-plugin-storybook-demo. It may be included in Next.js as an official example but only when @next/plugin-storybook reach stability. Anyway, this question is on Next.js side at the moment more than SB.

@shilman Everything is happening in this file in Next packages/next/build/webpack-config.ts as far as I can tell. It will parse tsconfig and jsconfig to explicitely compute webpack alias based on the paths. I am asking Next people to get confirmation. Could it be worthy to add this in Storybook also? I’ll try to demo that but it’ll take a bit of time.

Next webpack “magic” provides an examplary developer experience, while being rather safe and robust to change since it is based on standard features of jsconfig.json and tsconfig.json files. Webpack is more obfuscated within the framework, as it should be in the best of world. I wouldn’t mind have most of the same features in Storybook.

@shilman @valentinpalkovic I don’t know if it helps you or not, but I got it working by doing this: https://stackoverflow.com/a/68288720/7949258

In any case it might help someone having this problem (like me!) until you can fix it.

@valentinpalkovic @vanessayuenn This was raised on Twitter I’ve reproduced by editing the Storybook CLI template files:

// stories/Header.tsx
import React from 'react';
import { Button } from '@/stories/Button';

@/* is installed in the Next.js boilerplate by default, so I think we should prioritize fixing this in @storybook/nextjs. WDYT?

Here is how I solved.

I got the problem because I installed storybook in a Nextjs project that was using Shad cn ui.

On the component.json I replaced the @/ with src/

"aliases": {
-   "components": "@/components",
-   "utils": "@/lib/utils"
+   "components": "src/components",
+   "utils": "src/lib/utils"
  }

On the tsconfig.json I removed the paths and added baseUrl.

-"paths": {
-    "@/*": ["./src/*"]
-  }
+ "baseUrl": "."

After that, I went to the vscode search and replace tool (Ctrl + Shift + F);

And I replaced all @/components with src/components and all @/lib with src/lib.

After some errors and look a way to work with NEXT components, this config works. ./storybooks/main.js

  const path = require('path');
  
  webpackFinal: async (config, { configType }) => {
    config.resolve.modules = [
        path.resolve(__dirname, '..'),
        'node_modules',
    ];

    config.resolve.alias = {
        ...config.resolve.alias,
        components: path.resolve(__dirname, '../components'),
    };

    return config;
 }

Hey @eric-burel do you mind taking a look at this one?

This was fixed by https://github.com/storybookjs/storybook/pull/26651. The fix will be part of the 8.0.6 release!

Actually, tsconfig-paths-webpack-plugin is configured for Next.js. However, to activate the plugin, you have to define the baseUrl in your tsconfig.json. If baseUrl isn’t defined, the plugin doesn’t get initialized.

I’ve built a repro: https://github.com/lbke/next-sb-default-absolute-imports

I’ll try to see if we can make it work out of the box in Storybook by using the same pattern as Next.

In the meantime the official solution is to get the Storybook config right in order to have the same behavior as Next, as demonstrated in the comment above.