[RSC] Any advice on how to write unit tests when using server components? #14857
Replies: 0 comments 2 replies
-
|
This is a known pain point right now. React Router's Vite plugin handles the RSC module transforms (splitting server/client boundaries), and without it running in your test environment, the raw imports hit A few approaches that work today: 1. Use Vitest with React Router's Vite pluginConfigure Vitest to use the same Vite config so the RSC transforms are applied during tests: // vitest.config.ts
import { defineConfig } from 'vitest/config';
import { reactRouter } from '@react-router/dev/vite';
export default defineConfig({
plugins: [reactRouter()],
test: {
environment: 'node', // server components run on the server
},
});2. Mock the React Router importsIf you want to keep tests decoupled from the Vite plugin, mock the problematic imports: // vitest.setup.ts
vi.mock('react-router', async () => {
const actual = await vi.importActual('react-router');
return {
...actual,
// mock any server-incompatible exports
};
});3. Separate rendering logic from routingFor monorepo component libraries, extract your pure rendering components (no React Router imports) into separate modules that can be tested independently. Keep the route-aware wrapper thin and test it via integration/E2E tests instead. The Storybook RSC Vitest plugin ( |
Beta Was this translation helpful? Give feedback.
-
|
The core issue is that React Router's RSC integration relies on its Vite plugin to transform imports — separating client ( Solution 1: Add React Router's Vite plugin to your Vitest config// vitest.config.ts
import { defineConfig } from "vitest/config";
import { reactRouter } from "@react-router/dev/vite";
export default defineConfig({
plugins: [reactRouter()],
test: {
environment: "jsdom", // or "happy-dom"
// If testing server components, you may need:
server: {
deps: {
inline: ["react-router"],
},
},
},
});This ensures the same import transformations happen in tests as in dev/build. Solution 2: Mock React Router imports in server component testsIf adding the full plugin causes issues (e.g., in a monorepo package that doesn't have route definitions): // vitest.setup.ts or in the test file
vi.mock("react-router", async () => {
const actual = await vi.importActual("react-router");
return {
...actual,
// Mock client-only exports that fail in RSC context
useNavigate: vi.fn(),
useLocation: vi.fn(() => ({ pathname: "/", search: "" })),
useParams: vi.fn(() => ({})),
};
});Solution 3: Monorepo strategy — separate component and route testsFor your use case (components in packages, routes tested with Playwright): For pure UI components that don't import from For server components that use React Router features: // packages/shared/vitest.config.ts
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
test: {
environment: "jsdom",
setupFiles: ["./vitest.setup.ts"],
},
resolve: {
conditions: ["react-server"], // Enable RSC resolution
},
});The For Storybook's RSC pluginThe import { setProjectAnnotations } from "@storybook/react";
import * as projectAnnotations from "./preview";
setProjectAnnotations(projectAnnotations);And in your Storybook Vitest config, include the react-router plugin: // .storybook/vitest.config.ts
import { storybookTest } from "@storybook/experimental-addon-test/vitest-plugin";
import { reactRouter } from "@react-router/dev/vite";
export default defineConfig({
plugins: [storybookTest(), reactRouter()],
});TL;DR: The root cause is that React Router's RSC import transformations aren't applied during testing. Either add the Vite plugin to your Vitest config, use the |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I'm trying to write some unit tests using Vitest and the Storybook plugin for RSC and Vitest browser mode.
https://github.com/storybookjs/vitest-plugin-rsc
It doesn't work as the react router imports have errors around accessing client side code such as createContext. I'm assuming the react router vite plugin for RSC is changing imports somehow to make things safe for RSC, but when writing unit tests I can't (or don't know how) to use the react router plugin.
Any advice on how to do this? Ideally I want to have components live in separate packages in a monorepo with unit tests and have the actual react router site have tests with Playwright.
Beta Was this translation helpful? Give feedback.
All reactions