Storybook ".storybook/main.js SyntaxError" in CI
Storybook loads .storybook/main.js before anything else. A SyntaxError there stops the build immediately, most often because the file uses ESM import/export while Node is treating it as CommonJS, or the reverse.
What this error means
The build fails with "SyntaxError: Cannot use import statement outside a module" or "SyntaxError: Unexpected token 'export'" pointing at .storybook/main.js.
.storybook/main.js:1
export default {
^^^^^^
SyntaxError: Unexpected token 'export'Common causes
ESM syntax in a CommonJS context
The config uses export default {} but the package has no "type": "module" and the file is .js, so Node parses it as CommonJS.
A stray typo or unsupported syntax
A missing bracket, or top-level await in a context that does not support it, produces a parse error before Storybook can start.
How to fix it
Match the module format to your package
- Decide ESM or CJS based on
"type"inpackage.json. - For CJS, use
module.exports = {}; for ESM, keepexport default {}or rename to.mjs. - Re-run the build so the config parses.
// .storybook/main.cjs (CommonJS project)
module.exports = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
framework: { name: '@storybook/react-vite', options: {} },
};Use a .ts or .mjs config for ESM
Storybook 7 and 8 support main.ts; renaming avoids the ambiguity of .js in a CommonJS package.
// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(ts|tsx)'],
framework: { name: '@storybook/react-vite', options: {} },
};
export default config;How to prevent it
- Keep the config module format consistent with
package.json"type". - Prefer
main.tsso type checking catches config errors early. - Run
storybook buildlocally after editing the config.