Introduction
In this blog, I will walk you through why MSW mocking is useful, and how to implement it in your React.js application.
The problem
Let’s say you’re building a feature that consumes data from a backend API, but:
- The backend isn’t ready yet
- You’re having CORS issues, and the backend developer took a sick day :(
- You’re on a tight deadline
In most cases, you might just mock the data using a single constant.
But there’s a better approach.
The Solution: Mock Service Worker (MSW)
MSW works by intercepting actual network requests at the service worker level. What does that means?
- Your application thinks it’s communicating with a real API
- Your mocks work both in the browser and in tests
- You can simulate everything from successful responses to errors, timeouts, and more. This is very important
I can tell that it’s the closest thing to working with the real thing, without actually depending on it.
Diagram
In the diagram above, we can identify total of three actors:
-
Browser -> Let’s imagine you have a page called
PrivacyPolicy.jsx
. This component uses a custom hook or a direct fetch/axios call to request data from the endpoint/v1/privacy-policy
. In default scenario, this request would hit backend API URL, but with our approach, this request is intercepted before it ever leaves the browser and it does not hit backend. MSW service worker -> This worker is registered and running in the browser. It listens to all requests coming from you application. When your React app makes a request to
/v1/privacy-policy
, the MSW service worker intercepts it and checks if a corresponding mock handler is defined. It does this by referring to your mock definition file (commonly named handlers.js).Handlers.js -> This file contains a list of all mocked endpoints and how they should respond. If a matching handler is found, the service worker returns the mocked response as if it came from the real server. Your React component receives the data and renders the UI.
Key benefits
Fully isolated UI development - With MSW, your frontend development is no longer blocked by backend availability.
Easier error state simulation (timeout, 500 errors, etc.) -> Simulating edge cases is crucial for building resilient UIs. MSW makes easy to:
Mock slow network responses (e.g. ctx.delay(3000)) to test loading states.
Simulate server errors (e.g. 500, 401, 404) to test error handling.
Validate how your UI reacts to flaky connections, unauthorized access, or unexpected data.
- Shared, reusable mocks for testing and Storybook
Hot to implement it?
- Install the package
npm i msw
- Define your handlers
Create a
mocks/handlers.js
file
// handlers.js
import { http, HttpResponse } from 'msw';
export const handlers = [
http.get(`${API_URL}/v1/privacy-policy`, async () => {
return HttpResponse.json({
documentBody: `
<h1>Privacy Policy</h1>
<p>We value your privacy and handle your data responsibly.</p>
`
});
}),
];
- Setup the service worker
Create a mocks/browser.js
file
import { setupWorker } from 'msw';
import { handlers } from './handlers';
export const worker = setupWorker(...handlers);
- Modify your
.env
file
VITE_ENABLE_MOCKING=true
- Modify your
main.jsx
file (just add "enableMocking" method)
// main.tsx
// imports {}
async function enableMocking() {
if (import.meta.env.VITE_ENABLE_MOCKING !== 'true') return;
const { worker } = await import('./mocks/browser');
return worker.start();
}
enableMocking().then(() => {
const rootElement = document.getElementById('root');
if (!rootElement) throw new Error('Root element not found');
createRoot(rootElement).render(
<StrictMode>
</StrictMode>
);
});
Note: If you plan to use it also in test environment, be sure to create a mocks/node.js
file:
// mocks/server.jsx
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);
For more informations, you can check out official MSW documentation.
Bonus 🎁
I’m sharing template starter for handlers.js file that I use in my projects.
It includes mocked api endpoints, use of zod schemas for validation, delays and much more!
Connect with me
Top comments (2)
Thanks for this writeup, Nikola!
If I may, one small correction to the diagram would be to place the "handlers" part within your app's box (because that's where they are). So that the request flow is this:
That is only if you want to approach this diagram from a strictly technical perspective. As a general illustration, yours works fine.
Yessir, this kind of hands-on stuff for React just makes my life so much easier.