Modern web development increasingly relies on API-driven components that fetch and display data from backend services. However, backend development often lags behind frontend implementation, creating dependencies that slow down the development process. Storybook combined with Mock Service Worker (MSW) provides a powerful solution for developing and testing components in isolation without requiring a live API.
Storybook is an open-source tool for building, testing, and documenting UI components in isolation. It provides a development environment where developers can render components with different props, states, and configurations without running the full application. Our web development services team regularly uses Storybook to accelerate component development workflows.
Mock Service Worker is a declarative API mocking library that uses the browser's Service Worker API to intercept network requests. When a component makes an HTTP request, MSW catches it before it leaves the browser and returns a configured response instead.
Component Isolation
Develop components without the complexity of the full application context
API Simulation
Mock REST and GraphQL responses without a real backend
State Testing
Test loading, error, and success states with controlled responses
Living Documentation
Generate component catalogs that help teams understand and reuse components
Setting Up MSW With Storybook
Integrating MSW with Storybook involves installing dependencies, generating service worker files, and configuring Storybook to initialize MSW and apply mock handlers to stories. The process is straightforward and well-supported by the official MSW Storybook addon.
Installation
npm install msw msw-storybook-addon --save-dev
npx msw init public/
The msw-storybook-addon provides convenient integration points that simplify the setup process. After running the initialization command, a mockServiceWorker.js file is created in the public directory and must be served by Storybook's static file configuration.
Configuration
In your .storybook/preview.ts, add the following configuration:
import { initialize, mswLoader } from 'msw-storybook-addon';
initialize({
onUnhandledRequest: 'bypass',
});
export const loaders = [mswLoader];
The onUnhandledRequest: 'bypass' option ensures that requests without matching handlers pass through to the real network rather than causing errors. This approach aligns with the MSW declarative mocking philosophy, which emphasizes transparent interception at the network level. For teams building comprehensive API integration solutions, this setup enables parallel frontend and backend development.
1import { initialize, mswLoader } from 'msw-storybook-addon';2import type { Preview } from '@storybook/react';3 4// Initialize MSW with bypass for unhandled requests5initialize({6 onUnhandledRequest: 'bypass',7});8 9const preview: Preview = {10 loaders: [mswLoader],11 // ... other preview configuration12};13 14export default preview;Creating Mock Handlers For REST APIs
REST API mocking with MSW involves defining handlers that match request patterns and return appropriate responses. Each handler specifies the HTTP method, URL pattern, and response function. This declarative approach, as demonstrated in the Thinkmill tutorial on Storybook and mock APIs, provides precise control over mock behavior.
REST Handler Structure
Handlers can extract URL parameters, query strings, and request bodies when constructing responses. This flexibility enables sophisticated mock behavior that mirrors actual API functionality. When working with RESTful API design, establishing consistent patterns for request handling improves maintainability across your codebase.
import { http, HttpResponse } from 'msw';
export const handlers = [
http.get('/api/users/:userId', ({ params }) => {
const { userId } = params;
return HttpResponse.json({
id: userId,
name: 'Jane Developer',
email: '[email protected]',
role: 'senior-engineer'
});
}),
http.post('/api/users', async ({ request }) => {
const data = await request.json();
return HttpResponse.json(
{ success: true, user: { ...data, id: 'new-user-id' } },
{ status: 201 }
);
})
];
This pattern allows you to simulate various API scenarios including error states, empty results, and delayed responses for comprehensive component testing.
Creating Mock Handlers For GraphQL APIs
GraphQL mocking follows a similar pattern to REST but operates on GraphQL operation names rather than URL patterns. This approach aligns with GraphQL's single-endpoint architecture and allows precise targeting of specific queries and mutations, as demonstrated in the Storybook documentation.
GraphQL Handler Syntax
import { graphql, HttpResponse } from 'msw';
export const handlers = [
graphql.query('GetUserProfile', ({ variables }) => {
const { userId } = variables;
return HttpResponse.json({
data: {
user: {
id: userId,
username: 'frontenddev',
bio: 'Building digital experiences',
avatarUrl: 'https://example.com/avatar.png',
posts: {
nodes: [
{ id: '1', title: 'Getting Started with MSW' },
{ id: '2', title: 'Storybook Best Practices' }
]
}
}
}
});
}),
graphql.mutation('CreatePost', ({ variables }) => {
const { title, content } = variables;
return HttpResponse.json({
data: {
createPost: {
id: 'new-post-id',
title,
content,
createdAt: new Date().toISOString()
}
}
});
})
];
The handler response returns data structured according to the GraphQL schema, matching exactly what the component expects from the real API. For teams implementing GraphQL solutions, MSW provides consistent mock behavior across development and testing environments.
1import type { Meta, StoryObj } from '@storybook/react';2import { ArticleList } from './ArticleList';3import { handlers } from '../../mocks/handlers';4 5const meta: Meta<typeof ArticleList> = {6 component: ArticleList,7};8 9export default meta;10type Story = StoryObj<typeof ArticleList>;11 12export const Default: Story = {13 render: () => <ArticleList />,14 parameters: {15 msw: { handlers },16 },17};18 19export const Loading: Story = {20 render: () => <ArticleList />,21 parameters: {22 msw: {23 handlers: [24 graphql.query('GetNewsArticles', () => {25 return new HttpResponse(null, { status: 200 });26 })27 ],28 },29 },30};31 32export const ErrorState: Story = {33 render: () => <ArticleList />,34 parameters: {35 msw: {36 handlers: [37 graphql.query('GetNewsArticles', () => {38 return HttpResponse.json(39 { errors: [{ message: 'Failed to load articles' }] },40 { status: 200 }41 );42 })43 ],44 },45 },46};Best Practices For Mock API Development
Maintaining API Contract Alignment
Mocks should accurately reflect the current state of API contracts. As backend implementations evolve, mocks must be updated to match new response structures, field names, and behaviors. Sharing mock handler files between frontend and backend teams creates a collaborative contract that both sides can reference, ensuring consistency across the development workflow.
Performance Optimization
While mocking eliminates network latency, handlers that perform complex operations or return large data sets can still impact performance. Complex response builders should be optimized, and mock data should be appropriately sized for the testing scenarios they support. Consider using lazy-loaded mock data for large datasets.
Organizing Mock Data Files
Separate mock data files from handler definitions improve maintainability:
// mocks/data/users.ts
export const mockUser = {
id: '123',
name: 'Jane Developer',
email: '[email protected]',
avatarUrl: 'https://example.com/avatar.png',
};
// mocks/handlers/users.ts
import { http, HttpResponse } from 'msw';
import { mockUser } from '../data/users';
export const userHandlers = [
http.get('/api/users/:userId', () => {
return HttpResponse.json(mockUser);
})
];
This separation of concerns makes it easier to maintain mock data and update responses as APIs evolve. Following these best practices for component testing ensures your mock API infrastructure scales sustainably with your project.
Frequently Asked Questions
Sources
- Storybook: Mocking Network Requests - Official documentation for MSW addon setup and configuration
- Mock Service Worker (MSW) Official Site - API mocking library documentation and best practices
- Thinkmill: Storybook and Mock APIs - Complete tutorial with code examples for React and GraphQL setup