Mock Service Worker (MSW) for mocking network requests
MSW is an API mocking library that relies on service workers to capture network requests and provide mocked data in response. The MSW addon brings this functionality into Storybook, allowing you to mock API requests in your stories.
MSW addon version compatibility
The current instructions are for v3 of the MSW addon. When updating msw-storybook-addon to v3 from v2, you can migrate your configuration and stories automatically using the codemod: npx msw-storybook-migrate. For more information, see the MSW migration guide at https://github.com/mswjs/msw-storybook-addon/blob/main/MIGRATION.md#from-2xx-to-3xx.
Setting up MSW addon in Storybook
To set up the MSW addon: (1) Install MSW and the MSW addon using the appropriate npm command; (2) If not already using MSW, generate the service worker file necessary for MSW to work; (3) Ensure the staticDirs property in your Storybook configuration includes the generated service worker file (in /public, by default); (4) Initialize the addon and register it for all stories with a project-level loader (if using CSF 3) or by adding the addon to preview.ts (if using CSF Next).
Angular MSW addon setup consideration
Angular projects will likely need to adjust the command to save the mock service worker file in a different directory, such as src, rather than the default location.
Mocking REST API requests with MSW in stories
The MSW addon allows you to write stories that mock REST API requests. You can define request handlers for mocked GET, POST, and other HTTP methods. Each story can be configured using beforeEach to define the request handlers for the mock server.
Mocking GraphQL requests with MSW in stories
The MSW addon allows you to write stories that mock GraphQL requests. You can define request handlers for GraphQL queries and mutations. Each story can be configured using beforeEach to define the request handlers for the mock server. The example uses Apollo Client to make network requests, though other libraries like URQL or React Query can apply the same principles.
Configuring MSW handlers at different levels
You can use beforeEach to define request handlers for the mock server at the story level (for individual stories), at the component (meta) level (to apply handlers to all stories in a file), or at the project level (in preview.ts) to apply handlers to all stories in the project.
Using msw parameter in CSF 3
If using CSF 3, you can also use the msw parameter to define handlers for a story, component, or project, in addition to using beforeEach.
MSW supports multiple HTTP libraries
While the example uses the fetch API to make network requests, the same MSW principles can be applied to mock network requests in Storybook when using different libraries such as axios.
Three ways to mock modules in Storybook
Storybook provides three ways to mock modules for stories: automocking, subpath imports, and builder aliases. Automocking is the most straightforward approach and is recommended for projects using Vite and Webpack builders. The other two methods require more setup but provide similar functionality.
Automocking with sb.mock utility
Automocking uses the `sb.mock` utility function to register modules you want to mock. You register modules in your project-level configuration at `.storybook/preview.*` to ensure consistent and performant mocking across all stories. You can register both local modules (e.g., `../lib/session.ts`) and packages in `node_modules` (e.g., `uuid`).
Path requirements for local module mocking
When registering a mock for a local module, the path must not use an alias or subpath import (e.g., `@/lib/session.ts` or `#lib/session`), must be relative to the `.storybook/preview.*` file, and must include the file extension (e.g., `.ts` or `.js`). If using TypeScript, you can wrap the module path in `import()` to ensure the module is correctly resolved and typed.
Spy-only mocking approach
Spy-only mocking is recommended for most cases. Set the `spy` option to `true` when registering a mocked module. This leaves the original module's functionality intact while still allowing you to modify the behavior if needed and make assertions in your tests.
Fully automocked modules behavior
Fully automocked modules are registered by setting the `spy` option to `false` (or omitting it, as that is the default). This automatically replaces all exports from the module with Vitest mock functions, allowing you to control their behavior and make assertions. However, the module is still evaluated along with its dependencies, so side effects will still occur.
Automatic restoration of fn mocks
It is not necessary to restore `fn()` mocks with the cleanup function, as Storybook will already do that automatically before rendering a story. See the `parameters.test.restoreMocks` API for more information.
Mock file structure and placement
Mock files should be placed in a `__mocks__` directory next to the module you want to mock and 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. For packages in `node_modules`, create a `__mocks__` directory in the root of your project.
Mock files must use JavaScript and ESModules
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.
Vitest mock function methods for mocked modules
Mocked functions created with the `sb.mock` utility are full Vitest mock functions. Common useful methods include: `mockReturnValue(value)` to set the return value; `mockResolvedValue(value)` to set the value a mocked async function resolves to; and `mockImplementation(fn)` to set a custom implementation for the mocked function.
Automocking in development mode
In development mode, automocking relies on Vite's module graph invalidation. When a mock is added, changed, or removed in `.storybook/preview.*` or the `__mocks__` directory, the plugin intelligently invalidates all affected modules and triggers a hot reload, providing a fast and interactive development experience.
Automocking build-time processing
During build, a Vite plugin called viteMockPlugin scans `.storybook/preview.*` for all `sb.mock()` calls. If a corresponding file is found in the top-level `__mocks__` directory, that file is loaded and transformed by Vite. If no `__mocks__` file is found, the original module's code is transformed at build-time to replace its exports with mocks or spies. All mocking decisions and transformations happen at build time, resulting in no performance penalty.
Mocking scope differences from Vitest
Storybook's automocking has key differences from Vitest: mocks are global and defined only in `.storybook/preview.*` (you cannot call `sb.mock()` inside individual story files); all mocking decisions are finalized at build time making the system robust but less dynamic; you can still control behavior at runtime within a play function or `beforeEach` hook; there is no `sb.unmock()` or equivalent as the module graph is fixed in a production build.
sb.mock does not accept factory functions
The `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.
Subpath imports for mocking
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 Vite and Webpack builders. 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.
Subpath import mock file requirements
When creating a mock file for subpath imports, it must import the original module using a relative import (using a subpath or alias import would result in it importing itself). It should re-export all exports from the original module, use the `fn` utility to mock any necessary functionality, use the `mockName` method to ensure the name is preserved when minified, and should not introduce side effects that could affect other tests or components.
Wrapping external modules for subpath imports
You cannot directly mock an external module like `uuid` or `node:fs`. Instead, wrap it in your own module, which you can mock like any other internal one. For example, create a file `lib/uuid.ts` that imports and re-exports from the external module, then create a mock file `lib/uuid.mock.ts` for the wrapper using the `fn` utility.
Subpath imports package.json configuration
Configure subpath imports by defining the `imports` property in your project's `package.json` file. This property maps the subpath to the actual file path. Each module's entry should include `storybook`, `test`, and `default` keys. The `storybook` value is used when loaded in Storybook, while the `default` value is used when loaded in your project. The `test` condition is also used within Storybook, allowing the same configuration in Storybook and other tests.
TypeScript moduleResolution for subpath imports
Subpath imports will only be correctly resolved and typed when the `moduleResolution` property is set to `'Bundler'`, `'NodeNext'`, or `'Node16'` in your TypeScript configuration. If you are currently using `'node'`, that is intended for projects using Node.js version older than v10.
TypeScript type safety for mocked functions
When writing stories in TypeScript with subpath imports, you must import your mock modules using the full mocked file name to have the functions correctly typed in your stories. You do not need to do this in your component files; that is what the subpath import is for.
Using mocked utility for type-safe mocks
If writing your stories in TypeScript, you can use the `mocked` utility from `storybook/test` to ensure that mocked functions are correctly typed in your stories. This utility is a type-safe wrapper around the Vitest `vi.mocked` function.
beforeEach for mock setup and cleanup
Use the asynchronous `beforeEach` function to perform setup before the story renders (e.g., configure the mock behavior). This function can be defined at the story, component (which runs for all stories in the file), or project level (in `.storybook/preview.*`, which runs for all stories in the project). You can return a cleanup function from `beforeEach` which will be called after your story unmounts.
Webpack automocking ESM requirement
If you are using the Webpack builder, you can only automock `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.
exports is not defined error with Webpack
Webpack 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.
Builder aliases for module mocking
If your project is unable to use automocking or subpath imports, you can configure your Storybook builder to alias the module to a mock file. This instructs the builder to replace the module with the mock file when bundling your Storybook stories. Usage of the aliased module in stories is similar to subpath imports, but you import the module using the alias instead of the subpath.
Mocking conflicts with other testing tools
If you have 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. To address this, verify which tool is responsible for mocking a particular module and ensure the configurations do not overlap.
Mock files for deeper import paths
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 in automocking, or create a corresponding mock file (e.g., `__mocks__/lodash-es/add.js`) in the root of your project when using mock files.
Root __mocks__ directory in Vite projects
In 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.
Root __mocks__ directory in Webpack projects
In 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.
Mocking external dependencies in Storybook
To mock imports in Storybook, register module mocks in .storybook/preview.ts using sb.mock() with the spy option. Example: sb.mock(import('some-library'), { spy: true });. Use file extensions when referring to relative files: sb.mock(import('./relative/module.ts'), { spy: true });
Specify mock values per-story using beforeEach
Override mock behavior per-story using beforeEach and the mocked() type function. Import mocked from 'storybook/test' and use it to set mock values in the beforeEach hook before the story runs.
Mock imports example
Example of mocking and using mocked values in a story: import { expect, mocked, fn } from 'storybook/test'; import { library } from 'some-library'; const meta = { component: AuthButton, beforeEach: async () => { mocked(library).mockResolvedValue({ user: 'data' }); } }; export const LoggedIn: Story = { play: async ({ canvas }) => { await expect(library).toHaveBeenCalled(); } };