Introduction
Migrating from a client-side Vite project to the Next.js framework is a major step in a project's lifecycle. While developers often focus on shifting from react-router-dom to the Next.js App Router or converting index.html logic into layout.tsx, the testing suite is frequently overlooked.
If you were previously using Vitest—the lightning-fast testing framework powered by Vite—you might be wondering if you have to switch to Jest. The good news is that you don't. However, the migration requires specific configuration adjustments to account for Next.js features like Server Components, environment variables, and the Image component.
In this guide, we will walk through adapting your existing Vitest setup to work seamlessly within a Next.js environment.
Why Keep Vitest?
For many developers, Vitest is the preferred choice over Jest due to its native ESM support, shared configuration with the Vite pipeline, and superior performance in watch mode. Even when your production build moves to Webpack or Turbopack (via Next.js), you can continue using Vitest for your unit and integration tests to maintain developer velocity.
Step 1: Installing the Correct Dependencies
To bridge the gap between Vite's ecosystem and Next.js, you need a few additional packages. Next.js doesn't provide a built-in Vitest configuration, so you'll need the following:
npm install -D vitest @vitejs/plugin-react jsdom @testing-library/react @testing-library/jest-dom
Ensure you have @vitejs/plugin-react installed even though you are using Next.js. Vitest uses this plugin to transform your JSX/TSX files during the test run.
Step 2: The vitest.config.ts Setup
In a standard Vite project, your config is often simple. In Next.js, you must explicitly handle path aliases (like @/*) and ensure the environment mimics a browser.
Create a vitest.config.ts file in your root directory:
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: './vitest.setup.ts',
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});
Step 3: Handling Next.js Specifics
One of the biggest challenges when migrating is that Next.js components often rely on internal modules like next/router, next/navigation, or next/image.
Mocking the Router
If you use the App Router, you'll need to mock useRouter, usePathname, and useSearchParams. Since these are client hooks, Vitest can handle them, but they will return undefined or throw errors if not mocked during a test run.
// vitest.setup.ts
import '@testing-library/jest-dom';
import { vi } from 'vitest';
vi.mock('next/navigation', () => ({
useRouter: () => ({
push: vi.fn(),
replace: vi.fn(),
prefetch: vi.fn(),
}),
usePathname: () => '/',
useSearchParams: () => new URLSearchParams(),
}));
Dealing with Server Components (RSC)
Vitest runs in a Node environment but usually targets JSDOM. Testing React Server Components directly in Vitest is tricky because they are designed to run only on the server. The common strategy is to extract the logic into pure functions or "Client Component" wrappers that can be easily tested.
If you are handling a large-scale transition, using a specialized tool like ViteToNext.AI can help automate the structural changes of your components, making it easier to see where your test logic needs to be decoupled from the UI.
Step 4: Environment Variables
Next.js uses .env.local, .env.development, and .env.production. Vitest doesn't load these by default in the same way Next.js does. You can use the dotenv package or the built-in Vitest env configuration to ensure your tests have access to the necessary keys.
// vitest.config.ts extension
test: {
env: {
NEXT_PUBLIC_API_URL: 'http://localhost:3000',
},
}
Step 5: Handling Next/Image
The next/image component is notorious for breaking tests because it performs complex optimization. The simplest way to handle this is to mock it out with a standard img tag.
vi.mock('next/image', () => ({
__esModule: true,
default: (props: any) => <img {...props} />,
}));
Conclusion
Adapting your Vitest setup for Next.js allows you to keep the fast feedback loop you enjoyed in Vite while leveraging the powerful features of the Next.js framework. By correctly mocking the navigation hooks, setting up path aliases, and handling images, you can ensure your migration doesn't result in a broken CI pipeline.
Remember to verify your tests frequently during the migration process. Testing is your safety net; the more robust your Vitest setup, the more confident you'll be in your new Next.js architecture.
Further reading: ViteToNext.AI Migration Guide
Top comments (0)