npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@soroush.tech/vite-plugin-msw-server

v1.0.3

Published

Make server-side rendering deterministic in end-to-end tests: an msw/node mock server inside Vite, so SSR loaders and SSG prerendering resolve against your mocks instead of a live API.

Readme

@soroush.tech/vite-plugin-msw-server

npm version npm downloads coverage unpacked size types included license

🎭 Made for end-to-end tests

Mock the server side of your app in your e2e suite. Point Playwright or Cypress at vite dev (or a prerendered build) and your SSR data loaders resolve against msw handlers instead of a live API — no network flake, no seeded backend, reproducible in CI. → Testing with Playwright

A Vite plugin that starts an msw/node mock server inside the Vite process, so server-side data fetching resolves against your mocks during vite dev (SSR data() hooks) and vite build (SSG prerendering) — the rendering that happens in Node, where msw's browser worker can't reach.

It mirrors the browser-side msw worker for the server. The plugin has no runtime dependency on msw — you own the msw install and pass your server in, so it stays decoupled from your mock setup.

Install

# npm
npm install -D @soroush.tech/vite-plugin-msw-server msw
# pnpm
pnpm add -D @soroush.tech/vite-plugin-msw-server msw
# yarn
yarn add -D @soroush.tech/vite-plugin-msw-server msw

vite is a peer dependency (^6 || ^7 || ^8) and msw is yours to install — the plugin carries neither at runtime.

Usage

Gate it behind an env flag so production builds opt out and never load msw:

// vite.config.ts
import { defineConfig } from 'vite'
import mswServer from '@soroush.tech/vite-plugin-msw-server'

const mockServerEnabled = process.env.VITE_APP_MSW_ACTIVE === 'true'

export default defineConfig({
  plugins: [
    mswServer({
      enable: mockServerEnabled,
      // Factory ⇒ the mock module is only imported when the plugin actually runs.
      server: () => import('./src/test/mocks/server').then((m) => m.server),
    }),
  ],
})

Where ./src/test/mocks/server is your own:

import { setupServer } from 'msw/node'
import { handlers } from './handlers'

export const server = setupServer(...handlers)

Testing with Playwright

Start vite dev (or vite preview on a prerendered build) with the mock flag on and point Playwright at it — SSR resolves against your handlers, so tests never hit a live API:

// playwright.config.ts
import { defineConfig } from '@playwright/test'

export default defineConfig({
  webServer: {
    command: 'vite dev',
    env: { VITE_APP_MSW_ACTIVE: 'true' },
    url: 'http://localhost:5173',
    reuseExistingServer: !process.env.CI,
  },
})

Options

| Option | Type | Default | Description | | -------------------- | --------------------------------------------------------- | ---------- | ----------------------------------------------------------------------------------- | | server | MockServer \| (() => MockServer \| Promise<MockServer>) | — | An msw/node server, or a factory returning one. Use a factory to keep imports lazy. | | enable | boolean | true | When false, the plugin is an inert no-op. | | onUnhandledRequest | 'bypass' \| 'warn' \| 'error' | 'bypass' | Forwarded to server.listen. |

Server-side rendering & static generation

This plugin exists for the server side of a Vite app — the rendering that happens in Node, where msw's browser worker can't reach. Two flavors:

  • SSR (Server-Side Rendering) — HTML is rendered on a server. During vite dev, your SSR entry runs inside the Vite dev server (a Node process), and any server-side data fetching (route loaders, data() hooks) hits your real API by default. The plugin intercepts those fetches and answers them from your handlers instead.
  • SSG / prerendering (Static Site Generation) — during vite build, pages are rendered once at build time into static HTML. The same server-side fetching runs in the build process. The plugin makes that deterministic: prerendering resolves against mocks, so your static output never depends on a live API being reachable at build time. This holds when the framework prerenders inside the Vite build (Astro, Vike, React Router v7, vanilla Vite SSR). Some frameworks prerender in a separate process (SvelteKit, Nuxt) — there the build step isn't covered even though vite dev is. See the support matrix.

Both happen inside the Vite/Node process, which is exactly where the plugin's msw/node server lives (it starts on buildStart for builds and configureServer for dev). That is the whole scope:

✅ dev-time SSR and build-time SSG/prerender — ❌ not your deployed production server.

At runtime in production, Vite isn't running, so the plugin isn't either. That's by design — keep enable off for production builds (and because server is a factory, msw is never even imported when disabled).

Framework support

The plugin only affects requests made in the same process as Vite. Whether that covers your SSR/prerender depends on where the framework runs them:

| Framework | vite dev (SSR) | vite build (SSG) | Notes | | ------------------------------------------ | :--------------: | :----------------: | --------------------------------------------------------------------- | | Astro | ✅ | ✅ | prerender runs inside the Vite build | | Vike | ✅ | ✅ | prerender runs inside the Vite build | | React Router v7 | ✅ | ✅ | prerender runs inside the Vite build | | vanilla Vite SSR | ✅ | ✅ | when your prerender runs in the build process | | SvelteKit | ✅ | ❌ | prerender runs in a separate process | | Remix v2 (Vite) | ✅ | — | no in-Vite prerender; use React Router v7 for SSG | | Nuxt | ❌ | ❌ | Nitro SSR is a separate process — start msw in a Nitro plugin instead | | Next.js | ❌ | ❌ | not Vite-based — use msw via instrumentation.ts |

Runnable examples for the supported frameworks — Astro, Vike, React Router v7, SvelteKit, and Remix — live in the examples repo.

  • Runtime: Node.jsmsw/node intercepts Node's HTTP layer, so it works in local dev, CI, and the build. Edge/Workers production runtimes are not a target (and don't need to be — the plugin is dev/build-time only).
  • Pairs with: the msw browser worker (setupWorker) for client-side requests. This plugin covers the server half; together they mock both sides of an isomorphic app.

FAQ

Does this run in production? No. It only runs inside vite dev and the vite build prerender step. Gate it with enable so production builds skip it; your deployed runtime never includes the plugin or msw.

Does it mock requests in the browser? No — it's server-side only (msw/node). It covers SSR loaders and SSG prerendering. For client-side mocking, run msw's browser worker (setupWorker) separately. The two are complementary: this plugin mirrors the browser worker on the server.

Why doesn't the plugin depend on msw? So it stays decoupled from your mock setup and msw version. You install msw and pass your own setupServer(...) instance (or a factory). The plugin only needs the structural listen() shape — nothing more.

Why pass a factory instead of the server directly? A factory (() => import('./mocks/server')) keeps the mock module out of the config's module graph until the plugin actually runs. A production build with enable: false then never imports msw or your handlers.

My server-side requests aren't being mocked — what should I check? Confirm enable is true, that your fetching happens in the Node process (not a separate edge runtime), and that the request has a matching handler. Set onUnhandledRequest to 'warn' or 'error' to surface requests that fall through with no handler. Also confirm your framework renders in the Vite process — if SSR or prerender runs in a separate process (SvelteKit build, Nuxt/Nitro), the plugin can't reach it; see the support matrix.

Does it work with Nuxt? No. Nuxt's SSR runs in a separate Nitro process, so the plugin — which lives in the Vite process — can't intercept those requests. Start msw from a Nitro plugin instead.

Does it work with SvelteKit or Remix? Dev-time SSR, yes. Their production and prerender steps run outside the Vite process (SvelteKit prerenders separately; Remix serves via remix-serve), so the build isn't covered. See the support matrix.

Does it work with React Router v7? Yes — fully. Its prerender option runs loaders inside the Vite build, so both dev SSR and build-time SSG are covered, the same as Astro and Vike. It's the way to get plugin-covered SSG in the React ecosystem (Remix v2's remix-ssg uses the classic, non-Vite compiler, which the plugin can't hook).

Which Node versions are supported? Any Node that your msw version supports. msw/node hooks Node's HTTP layer, so it runs under Node.js for dev, CI, and builds.

Release notes

Per-version notes for every published release live in release-notes/.