Fixing "Functions cannot be passed directly to Client Components" in Next.js
Problem Statement
When working with Next.js (version 13+), you may encounter this runtime error:
Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server"
This error occurs when you:
- Attempt to pass a function from a server component to a client component
- Define interactive client functionality in a component not marked as client-only
- Use special Next.js files (like error boundaries) without proper client marking
The core issue stems from Next.js's strict separation of server and client components. Functions defined in server components cannot be passed directly to client components since they're not serializable. Next.js enforces this boundary to prevent security risks and ensure performance optimization.
Solutions
1. Mark Components as Client Components
For pure client-side functionality (event handlers, UI interactions), add "use client"
at the top of your component file.
"use client";
// Client-only component with interactivity
import { ProductProps } from "@/components/Products";
export default function SingleProduct({ props }: { props: ProductProps }) {
const imageLoader = ({ src }: any) => {
return `https://fakestoreapi.com/${src}`;
};
return (
<Image
src={props.image}
width={350}
height={350}
loader={imageLoader} // Works because component is client-only
alt={props.title}
/>
);
}
2. Use Server Actions for Server-Side Functions
For server-side logic called from client components, mark functions with "use server"
.
import ClientForm from "@/components/Form";
// Server component
export default function Home() {
// Server action with proper marking
const formAction = async (data: string) => {
"use server";
console.log("Server-side action:", data);
};
return <ClientForm action={formAction} />;
}
"use client";
// Client component
export default function Form({ action }: { action: Function }) {
return (
<form onSubmit={(e) => {
e.preventDefault();
action("Form data"); // Calls server action
}}>
{/* Form elements */}
</form>
);
}
3. Special File Handling for Error Boundaries
Next.js requires files like error.tsx
to be client components:
"use client"; // Required for error catching in Next.js
export default function Error({ error, reset }: {
error: Error;
reset: () => void
}) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
);
}
4. Reorganize Component Structure
For complex cases, restructure your architecture to:
Practical implementation:
"use server";
export async function deleteItem(itemId: string) {
"use server";
// Server deletion logic
}
"use client";
import { deleteItem } from "@app/actions";
export default function DeleteButton({ id }: { id: string }) {
return (
<button onClick={async () => await deleteItem(id)}>
Delete
</button>
);
}
Key Principles
- Component Hierarchy: Server components can import and render client components, but not vice versa
- Function Passing: Functions in server components can only be passed to client components via
"use server"
actions - Serialization Limits: Client components receive limited serializable types from server components:
- Primitive types (string, number, boolean)
- JSON-friendly objects
- JSX elements
- Server Actions (via
"use server"
)
- Special Files: Components like
error.tsx
,loading.tsx
, and interactive UI elements must be client components
Best Practices
Component Organization
- Keep server components at the top of your component tree
- Push client interactivity down to leaf components
- Use
"use client"
sparingly - avoid blanket conversions
Server Actions
- Add
"use server"
to individual async functions - Place server actions in separate files for reusability
- Ensure server actions aren't passed to client-specific dependencies
- Add
Debugging Tip: When encountering this error:
- Identify the function being passed
- Determine if it should run on server or client
- Add proper directives (
"use client"
or"use server"
) - Verify component hierarchy doesn't mix contexts improperly
Version Compatibility
This pattern requires Next.js 13+ with the App Router, as server components and actions weren't available in previous versions.
Server Actions Pre-Release
As of Next.js 13.4, Server Actions are still experimental. Enable them in your next.config.js
:
module.exports = {
experimental: {
serverActions: true,
},
};