I have a turborepo with a react component library which I publish to NPM. Call it @myscope/react
— it's bundled with CSS using RollupJS. And turbo has some good docs on publishing local packages here.
In the repo, I also have two apps, a website that uses the React component library and a Storybook. The components are imported correctly by the webapp, and TypeScript is happy even in the Storybook directory. But when Storybook runs, I get:
The requested module '/@fs/Users/{user}/mre/packages/react/dist/index.js' does not provide an export named 'MyComponent'
Why? I've looked at dist/index.js
and it seems like it's exported, but is this a limitation of the module strategy that Storybook supports?
I've created a minimum reproduction of the error here.
.storybook/main.ts
:
import type { StorybookConfig } from "@storybook/react-vite";
import { join, dirname } from "path";
/**
* This function is used to resolve the absolute path of a package.
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
*/
function getAbsolutePath(value: string): any {
return dirname(require.resolve(join(value, "package.json")));
}
const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
addons: [
getAbsolutePath("@storybook/addon-onboarding"),
getAbsolutePath("@storybook/addon-links"),
getAbsolutePath("@storybook/addon-essentials"),
getAbsolutePath("@chromatic-com/storybook"),
getAbsolutePath("@storybook/addon-interactions"),
],
framework: {
name: getAbsolutePath("@storybook/react-vite"),
options: {},
},
};
export default config;
Storybook package.json
:
{
"name": "my-storybook",
"private": true,
"version": "0.1.2",
"type": "module",
"scripts": {
"dev": "storybook dev -p 6006",
"build": "storybook build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"@myscope/react": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"storybook": "^8.2.7"
},
"devDependencies": {
"@chromatic-com/storybook": "^1.6.1",
"@storybook/addon-essentials": "^8.2.7",
"@storybook/addon-interactions": "^8.2.7",
"@storybook/addon-links": "^8.2.7",
"@storybook/addon-onboarding": "^8.2.7",
"@storybook/blocks": "^8.2.7",
"@storybook/react": "^8.2.7",
"@storybook/react-vite": "^8.2.7",
"@storybook/test": "^8.2.7",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.15.0",
"@typescript-eslint/parser": "^7.15.0",
"@vitejs/plugin-react": "^4.3.1",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.7",
"eslint-plugin-storybook": "^0.8.0",
"tsconfig": "workspace:*",
"typescript": "^5.2.2",
"vite": "^5.3.4"
}
}
vite.config.ts
:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})
I have reviewed this discussion and I proceeded anyway. But I would love to at least understand why this doesn't work.
For my esteemed colleagues voting to close my question without comment due to insufficient debugging info when I have linked a minimum reproduction GitHub repo, here's a screenshot of the error I'm receiving at http://localhost:6006/?path=/docs/example-mycomponent--docs
I would expect the component to render as it does at localhost:3000
The problem is your apps/storybook/package.json
file uses "type": "module"
and is built with vite so defaults to using ES modules, but your packages/react/rollup.config.js
specifies a build output of format: 'cjs'
.
All you have to do is change to format: 'es'
and your problem goes away.
So you have to decide if you still want your previous cjs
build, or es
, or a dual build of packages/react
.
Changing to format: 'es'
is the simplest path. A dual build would be the next simplest, but I would recommend using exports
in the packages/react/package.json
to better support that. You can also probably get vite to support commonjs by adding a babel preset to the @vitejs/plugin-react
configuration but I wouldn't take this path.
Dual build is the most versatile, but you probably really don't need CJS either.