docs/writing-stories/mocking-data-and-modules/mocking-modules.mdx
Components often depend on other modules, such as other components, utility functions, or libraries. These can be from external packages or internal to your project. When rendering those components in Storybook or testing them, you may want to mock those modules to control their behavior and isolate the component's functionality.
For example, this simple component depends on two modules, a local utility function to access the user's browser session and an external package to generate a unique ID:
import { v4 as uuidv4 } from 'uuid';
import { getUserFromSession } from '../lib/session';
export function AuthButton() {
const user = getUserFromSession();
const id = uuidv4();
return (
<button onClick={() => { console.log(`User: ${user.name}, ID: ${id}`) }}>
{user ? `Welcome, ${user.name}` : 'Sign in'}
</button>
);
}
The above example is written with React, but the same principles apply to other renderers like Vue, Svelte, or Web Components. The important part is the usage of the two module dependencies.
When writing stories or tests for this component, you may want to mock the getUserFromSession function to control the user data returned, or mock the uuidv4 function to return a predictable ID. This allows you to test the component's behavior without relying on the actual implementations of these modules.
For maximum flexibility, Storybook provides three ways to mock modules for your stories. Let's walk through each of them, starting with the most straightforward approach.
Automocking is the most straightforward way to mock modules in Storybook, and we recommend it for all projects using the Vite and Webpack builders (other builders must use one of the other techniques, below). This approach requires minimal configuration while allowing for flexible mocking of modules.
It works with two steps. First, register the modules you want to mock in your Storybook configuration. Then, control the behavior and make assertions about the mocked modules in your stories.
When automocking, you use the sb.mock utility function to register modules you want to mock. There are three ways to register modules: as spy-only, fully automocked, or with a mock file. Each method has its use cases and benefits.
There are some key details to keep in mind when using the sb.mock utility:
../lib/session.ts) and packages in node_modules (e.g., uuid)..storybook/preview.js|ts. This ensures consistent and performant mocking across all stories in your project. You can modify the behavior of these modules in your stories, but you cannot register them directly in the story files.@/lib/session.ts or #lib/session)..storybook/preview.js|ts file..ts or .js).import() to ensure the module is correctly resolved and typed. For example, sb.mock(import('../lib/session.ts')).node_module packages that have ESModules (ESM) entry points. If a module has both CommonJS (CJS) and ESM entry points, Webpack doesn't correctly resolve the ESM entry and it cannot be mocked. Webpack users can still mock CJS node_module packages by providing a mock file.For most cases, you should register a mocked module as spy-only, by setting the spy option to true. This leaves the original module's functionality intact, while still allowing you to modify the behavior if needed and make assertions in your tests.
For example, if you want to spy on the getUserFromSession function and the uuidv4 function from the uuid package, you can call the sb.mock utility function in your .storybook/preview.js|ts file:
If you need to mock an external module that has a deeper import path (e.g. lodash-es/add), register the mock with that path.
You can then control the behavior of these modules and make assertions about them in your stories, such as checking if a function was called or what arguments it was called with.
For cases where you need to prevent the original module's functionality from executing, set the spy option to false (or omit it, because that is the default value). This will automatically replace all exports from the module with Vitest mock functions, allowing you to control their behavior and make assertions while being certain that the original functionality never runs.
Fully automocked modules do not execute their exported functions, but the module is still evaluated, along with its dependencies. This means that if the module has side effects (e.g., modifying global state, logging to the console, etc.), those side effects will still occur. Similarly, a module written to run on the server will attempt to be evaluated in the browser. If you want to prevent the original module's code from running entirely, you should use a mock file instead.
</Callout>You can then control the behavior of these modules and make assertions about them in your stories, just like with the spy-only approach.
If you want to mock a module with more complex behavior or reuse a mock's behavior across multiple stories, you can create a mock file. This file should be placed in a __mocks__ directory next to the module you want to mock, and it should export the same named exports as the original module.
For example, to mock the session module in the lib directory, create a file named session.js|ts in the lib/__mocks__ directory:
export function getUserFromSession() {
return { name: 'Mocked User' };
}
For packages in your node_modules, create a __mocks__ directory in the root of your project and create the mock file there. For example, to mock the uuid package, create a file named uuid.js in the __mocks__ directory:
export function v4() {
return '1234-5678-90ab-cdef';
}
If you need to mock an external module that has a deeper import path (e.g. lodash-es/add), create a corresponding mock file (e.g. __mocks__/lodash-es/add.js) in the root of your project.
The root of your project is determined differently depending on your builder:
Vite projects
The root __mocks__ directory should be placed in the root directory, as defined in your project's Vite configuration (typically process.cwd()) If that is unavailable, it defaults to the directory containing your .storybook directory.
Webpack projects
The root __mocks__ directory should be placed in the context directory, as defined in your project's Webpack configuration (typically process.cwd()). If that is unavailable, it defaults to the root of your repository.
Mock files must be written with JavaScript (not TypeScript) using ESModules (not CJS).
They must export the same named exports as the original module. If you want to mock a default export, you can use export default in the mock file.
You can then use the sb.mock utility to register these mock files in your preview.js|ts file:
Note that the API for registering automatically mocked modules and mock files is the same. The only difference is that sb.mock will first look for a mock file in the appropriate directory before automatically mocking the module.
All registered automocked modules are used the same way within your stories. You can control the behavior, such as defining what it returns, and make assertions about the modules.
<CodeSnippets path="automocked-modules-in-story.md" />Mocked functions created with the sb.mock utility are full Vitest mock functions, which means you can use all the methods available on them. Some of the most useful methods include:
| Method | Description |
|---|---|
mockReturnValue(value) | Sets the return value of the mocked function. |
mockResolvedValue(value) | Sets the value the mocked async function resolves to. |
mockImplementation(fn) | Sets a custom implementation for the mocked function. |
If you are writing your stories in TypeScript, you can use the mocked utility from storybook/test to ensure that the mocked functions are correctly typed in your stories. This utility is a type-safe wrapper around the Vitest vi.mocked function.
Storybook's automocking is built on Vitest's mocking engine. The behavior adjusts depending on whether you're in development mode or build mode:
Dev Mode
In dev mode, mocking relies on Vite's module graph invalidation. When a mock is added, changed, or removed (either in .storybook/preview.js|ts or the __mocks__ directory), the plugin intelligently invalidates all affected modules and triggers a hot reload. This provides a fast and interactive development experience.
Dev and build mode
.storybook/preview.js|ts for all sb.mock() calls during the build process.__mocks__ redirects: If a corresponding file is found in the top-level __mocks__ directory, that file is loaded and transformed by Vite.__mocks__ file is found, the original module's code is transformed at build-time to replace its exports with mocks or spies.While this feature uses Vitest's mocking engine, the implementation within Storybook has some key differences:
.storybook/preview.js|ts. Unlike Vitest, you cannot call sb.mock() inside individual story files.sb.unmock() or equivalent, as the module graph is fixed in a production build.beforeEach hook (e.g., mocked(myFunction).mockReturnValue('new value')).sb.mock() API does not accept a factory function as its second argument (e.g., sb.mock('path', () => ({...}))). This is because all mocking decisions are resolved at build time, whereas factories are executed at runtime.If automocking is not suitable for your project, there are two alternative methods to mock modules in Storybook: subpath imports and builder aliases. These methods require a bit more setup but provide similar functionality to automocking, allowing you to control the behavior of modules in your stories.
You can use subpath imports, a Node feature, to mock modules. Subpath imports allow you to define custom paths for modules in your project, which can be used to replace the original module with a mock file. They work with both the Vite and Webpack builders.
To mock a module, create a file with the same name and in the same directory as the module you want to mock. For example, to mock a module named session, create a file next to it named session.mock.js|ts, with a few characteristics:
fn utility to mock any necessary functionality from the original module.mockName method to ensure the name is preserved when minifiedHere's an example of a mock file for a module named session:
When you use the fn utility to mock a module, you create full Vitest mock functions. See below for examples of how you can use a mocked module in your stories.
Mock files for external modules
You can't directly mock an external module like uuid or node:fs. Instead, you must wrap it in your own module, which you can mock like any other internal one. For example, with uuid, you could do the following:
import { v4 } from 'uuid';
export const uuidv4 = v4;
And create a mock for the wrapper:
import { fn } from 'storybook/test';
import * as actual from './uuid';
export const uuidv4 = fn(actual.uuidv4).mockName('uuidv4');
To configure subpath imports, you define the imports property in your project's package.json file. This property maps the subpath to the actual file path. The example below configures subpath imports for four internal modules:
There are three aspects to this configuration worth noting:
First, each subpath must begin with #, to differentiate it from a regular module path. The #* entry is a catch-all that maps all subpaths to the root directory.
Second, the order of the keys is important. The default key should come last.
Third, note the storybook, test, and default keys in each module's entry. The storybook value is used to import the mock file when loaded in Storybook, while the default value is used to import the original module when loaded in your project. The test condition is also used within Storybook, which allows you to use the same configuration in Storybook and your other tests.
With the package configuration in place, you can then update your component file to use the subpath import:
// ➖ Remove this line
// import { getUserFromSession } from '../../lib/session';
// ➕ Add this line
import { getUserFromSession } from '#lib/session';
// ...rest of the file
If you are currently using 'node', that is intended for projects using a Node.js version older than v10. Projects written with modern code likely do not need to use 'node'.
Storybook recommends the TSConfig Cheat Sheet for guidance on setting up your TypeScript configuration. </Callout>
When you use the fn utility to mock a module, you create full Vitest mock functions, which have many methods available. Some of the most useful methods include:
| Method | Description |
|---|---|
mockReturnValue(value) | Sets the return value of the mocked function. |
mockResolvedValue(value) | Sets the value the mocked async function resolves to. |
mockImplementation(fn) | Sets a custom implementation for the mocked function. |
Here, we define beforeEach on a story (which will run before the story is rendered) to set a mocked return value for the getUserFromSession function used by the Page component:
The fn utility also spies on the original module's functions, which you can use to assert their behavior in your tests. For example, you can use interaction tests to verify that a function was called with specific arguments.
For example, this story checks that the saveNote function was called when the user clicks the save button:
If your project is unable to use automocking or subpath imports, you can configure your Storybook builder to alias the module to the mock file. This will instruct the builder to replace the module with the mock file when bundling your Storybook stories.
<CodeSnippets path="module-aliases-config.md" />Usage of the aliased module in stories is similar to when using subpath imports in stories, but you import the module using the alias instead of the subpath.
Before the story renders, you can use the asynchronous beforeEach function to perform any setup you need (e.g., configure the mock behavior). This function can be defined at the story, component (which will run for all stories in the file), or project (defined in .storybook/preview.js|ts, which will run for all stories in the project).
You can also return a cleanup function from beforeEach which will be called after your story unmounts. This is useful for tasks like unsubscribing observers, etc.
Here's an example of using the mockdate package to mock the Date and reset it when the story unmounts.
exports is not defined errorWebpack projects may encounter an exports is not defined error when using automocking. This is usually caused by attempting to mock a module with CommonJS (CJS) entry points. Automocking with Webpack only works with modules that have ESModules (ESM) entry points exclusively, so you must use a mock file to mock CJS modules.
If you've already set up mocking with other testing tools (e.g., Jest), you may encounter conflicts when using Storybook's mocking system. These conflicts can cause unexpected behavior, errors, or incorrect mocks when both tools try to mock the same module. This is a known issue when sharing mock files or configurations between Storybook and other testing tools. To address this situation, we recommend that you verify which tool is responsible for mocking a particular module and make sure the configurations do not overlap to avoid any conflicts.