P PasteCode
Failure

How to Fix AI Putting Server Code in Client Components

AI agents leak database queries, secret env vars, and Node.js APIs into 'use client' components, exposing server-only logic to the browser bundle.

CursorClaude CodeCodexWindsurf Next.jsTypeScript
.md .json Updated Jun 8, 2026

The agent adds a "use client" directive to a component that imports Prisma, fs, or a secret env var — which ships that code to every visitor’s browser bundle.

The symptom

A component marked "use client" performs a direct database call or reads a process.env secret.

"use client";
import { db } from "@/lib/db"; // Prisma client — Node.js only
import { useState, useEffect } from "react";
export function UserCard({ id }: { id: string }) {
const [user, setUser] = useState(null);
useEffect(() => {
// db is a Node.js module — this will throw at runtime in the browser
db.user.findUnique({ where: { id } }).then(setUser);
}, [id]);
return <div>{user?.name}</div>;
}

The build may succeed (Next.js bundles the import) but the page throws a runtime error in the browser because @prisma/client requires Node.js APIs.

Why it happens

The agent sees that data is needed inside a component that also uses state or effects, and reaches for the data layer it knows. It does not model the server/client boundary or know that "use client" components cannot import Node.js-only modules.

How to spot it

  • "use client" at the top of a file that also imports from @/lib/db, prisma, fs, path, or crypto.
  • process.env.DATABASE_URL or any _SECRET_ variable read inside a "use client" file.
  • Build output contains Critical dependency: the request of a dependency is an expression or a Prisma bundle warning.
  • server-only package is absent from the project.

How to fix it

Split into a Server Component parent that fetches, and a Client Component child that handles interactivity.

// app/users/[id]/page.tsx — Server Component (no directive)
import { db } from "@/lib/db";
import { UserCard } from "./UserCard";
export default async function UserPage({ params }: { params: { id: string } }) {
const user = await db.user.findUniqueOrThrow({ where: { id: params.id } });
// Serialize — pass plain data, not the Prisma object
return <UserCard name={user.name} email={user.email} />;
}
// app/users/[id]/UserCard.tsx — Client Component
"use client";
import { useState } from "react";
export function UserCard({ name, email }: { name: string; email: string }) {
const [expanded, setExpanded] = useState(false);
return (
<div>
<p>{name}</p>
{expanded && <p>{email}</p>}
<button onClick={() => setExpanded((v) => !v)}>Toggle</button>
</div>
);
}
[ ] Install "server-only" and import it at the top of every server-side lib file
[ ] No db/prisma imports in "use client" files
[ ] No process.env secrets read in "use client" files
[ ] Server Component fetches data; Client Component receives plain props
[ ] Use Server Actions (not useEffect+fetch) when a client interaction needs db access

Fix Prompt

Fix Prompt
This "use client" component imports server-only modules (Prisma, fs, or secret
env vars). Refactor it: move all data fetching into an async Server Component
parent, pass only serializable props to the client component, and keep "use
client" only on the part that needs browser APIs or React state. Add
"server-only" to any shared lib files that must never reach the browser.

Test

Terminal window
# List all "use client" files that also import known server-only packages
grep -rl '"use client"' app/ | xargs grep -l "prisma\|@/lib/db\|\"fs\"\|\"path\"\|\"crypto\"" && echo "FAIL: server code in client component" || echo "OK"