I have a Next.js app that is running without any errors. However, I am using Vitest for my testing framework and when running a specific test (layout.test.tsx) it produces this error:
FAIL src/app/__tests__/layout.test.tsx [ src/app/__tests__/layout.test.tsx ]
Failed to load PostCSS config: Failed to load PostCSS config (searchPath: /Users/joe/my-app): [TypeError] Invalid PostCSS Plugin found at: plugins[0]
(@/Users/joe/my-app/postcss.config.mjs)
TypeError: Invalid PostCSS Plugin found at: plugins[0]
(@/Users/joe/my-app/postcss.config.mjs)
at file:///Users/joe/my-app/node_modules/vite/dist/node/chunks/dep-DBxKXgDP.js:11827:15
at Array.forEach (<anonymous>)
at plugins (file:///Users/joe/my-app/node_modules/vite/dist/node/chunks/dep-DBxKXgDP.js:11809:10)
at processResult (file:///Users/joe/my-app/node_modules/vite/dist/node/chunks/dep-DBxKXgDP.js:11876:20)
Plugin: vite:css
File: /Users/joe/my-app/src/app/globals.css
I have no idea what is causing this error and have search all over the internet and followed various suggestions. Here are some of the key files of the project:
postcss.config.mjs
const config = {
plugins: ["@tailwindcss/postcss"],
};
export default config;
vitest.config.mjs
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
plugins: [tsconfigPaths(), react()],
test: {
environment: "jsdom",
globals: true,
setupFiles: "./vitest.setup.mjs",
},
});
global.css
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}
layout.tsx
import type { Metadata } from "next";
import { Geist } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={`${geistSans.variable} antialiased`}>{children}</body>
</html>
);
}
layout.test.tsx
import { render, screen } from "@testing-library/react";
import RootLayout from "../layout";
describe("<RootLayout />", () => {
describe("WHEN the component is rendered", () => {
beforeEach(() => {
render(
<RootLayout>
<div>Some content</div>
</RootLayout>,
);
});
test("THEN the child component is displayed", () => {
const content = screen.getByText("Some content");
expect(content).toBeVisible();
});
});
});
Your PostCSS configuration is incorrect. It seems that in one of Next.js's pull requests, they fixed the PostCSS configuration in one of their Tailwind templates according to the response, but not all templates have been updated.
app-tw-empty/js
fixedapp-tw-empty/ts
fixedapp-tw/js
not fixedapp-tw/ts
not fixed - If I had to guess, this is probably what you used.If plugins is an array, it expects functions.
postcss.config.mjs
import tailwind from "@tailwindcss/postcss";
const config = {
plugins: [
tailwind(),
],
};
export default config;
If it is an object, the key defines the plugin, and additional options can be passed like this:
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;
Extra note: For TailwindCSS v4, it's important to use .mjs
. Also, make sure your project is set to module
type in the package.json
.
If the postcss.config.mjs
feels like a burden, you can also declare it in your package.json
:
{
"postcss": {
"plugins": {
"@tailwindcss/postcss": {}
}
}
}