How to Fix Next.js Server-Only Component Error in Storybook
Subpath imports to the rescue!
Published on
-/- lines long
If you're using Storybook in a Next.js project, you might be using the server-only
package(opens in new tab), as it's recommended in the Next.js docs(opens in new tab). However, this comes with a caveat — the error triggers in Storybook as well:

Error: This module cannot be imported from a Client Component module. It should only be used from a Server Component.
at ./node_modules/.pnpm/[email protected]/node_modules/server-only/index.js (http://localhost:6006/src_components_Icon_tsx.iframe.bundle.js:5727:7)
at options.factory (http://localhost:6006/runtime~main.iframe.bundle.js:684:31)
at __webpack_require__ (http://localhost:6006/runtime~main.iframe.bundle.js:28:33)
at fn (http://localhost:6006/runtime~main.iframe.bundle.js:343:21)
at ./src/components/Icon.tsx (http://localhost:6006/src_components_Icon_tsx.iframe.bundle.js:17:69)
at options.factory (http://localhost:6006/runtime~main.iframe.bundle.js:684:31)
at __webpack_require__ (http://localhost:6006/runtime~main.iframe.bundle.js:28:33)
at fn (http://localhost:6006/runtime~main.iframe.bundle.js:343:21)
at ./src/components/Button.tsx (http://localhost:6006/components-Button-stories.iframe.bundle.js:344:63)
at options.factory (http://localhost:6006/runtime~main.iframe.bundle.js:684:31)
The good news is that you can mock modules in Storybook(opens in new tab). This means that one module is loaded in the actual Next.js server (both in development and production mode), while a different one is loaded specifically for Storybook.
To fix this, we leverage the Node.js feature "Subpath imports"(opens in new tab) to create different mappings for the server-only
package. So you need this in your package.json
:
{
"imports": {
"#server-only": {
"storybook": "./.storybook/noop.ts",
"default": "server-only"
}
}
}
Now you only have to create the .storybook/noop.ts
file, which should just be an empty file. The whole idea is to avoid importing the server-only
module, i.e. perform no operation.
With this in place, all that's left is to edit your component to use the subpath import, rather then the real import:
- import "server-only";
+ import "#server-only";
export function Button(props) {
return <button>Click Me!</button>;
}
Now the following happens in your component:
-
Next.js maps
#server-only
to the actualserver-only
module and loads it, throwing an error when importing the wrong component at the wrong place, as it should. -
Storybook maps
#server-only
to./storybook/noop.ts
, which does nothing, as we don't really care about that warning in our React component workshop.
With this, you should be all set to create stories for your server components!