Skip to content

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:

  1. Attempt to pass a function from a server component to a client component
  2. Define interactive client functionality in a component not marked as client-only
  3. 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.

tsx
"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".

tsx
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} />;
}
tsx
"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:

tsx
"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:

tsx
"use server";

export async function deleteItem(itemId: string) {
  "use server";
  // Server deletion logic
}
tsx
"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

  1. Component Hierarchy: Server components can import and render client components, but not vice versa
  2. Function Passing: Functions in server components can only be passed to client components via "use server" actions
  3. 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")
  4. 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
  • Debugging Tip: When encountering this error:

    1. Identify the function being passed
    2. Determine if it should run on server or client
    3. Add proper directives ("use client" or "use server")
    4. 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:

javascript
module.exports = {
  experimental: {
    serverActions: true,
  },
};