I am getting an error when trying to pass a server component to a client component as props for a next-auth v5
in Next.js v14
implementation. I am trying to follow this pattern on the Next site https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns#supported-pattern-passing-server-components-to-client-components-as-props to pass a server component to a client component. The error is:
Error: async/await is not yet supported in Client Components, only Server Components. This error is often caused by accidentally adding
'use client'
to a module that was originally written for the server.
What needs to be altered to resolve this error?
The 3 components are as follows:
AuthClientInOutWrapper:
"use client";
import React from "react";
async function AuthClientInOutWrapper({
children
}: {
children: React.ReactNode;
}) {
return (
<>
{children}
</>
);
}
export default AuthClientInOutWrapper;
AuthServerInOutForms
"use server";
import { auth, signIn, signOut } from "auth";
async function AuthServerInOutForms() {
const session = await auth();
return (
<>
{session && session.user ? (
<>
<h1>sign out 44</h1>
<form
action={async () => {
await signOut();
}}
>
<button className="text-white" type="submit">Sign Out 44</button>
</form>
</>
) : (
<>
<h1>sign in 44</h1>
<form
action={async () => {
await signIn();
}}
>
<button className="text-white" type="submit">Sign In 44</button>
</form>
</>
)}
</>
);
}
export default AuthServerInOutForms;
Nav client component:
<AuthClientInOutWrapper>
<AuthServerInOutForms />
</AuthClientInOutWrapper>
Simplified Nav complete component:
'use client';
import React, { ReactNode, useState } from 'react';
import Link from 'next/link';
import AuthClientInOutWrapperCatto from '../UI-client-server/AuthClientInOutWrapperCatto/AuthClientInOutWrapperCatto';
import AuthServerInOutFormsCatto from '../UI-client-server/AuthServerInOutFormsCatto/AuthServerInOutFormsCatto';
const Nav = () => {
const [isNavOpen, setIsNavOpen] = useState(false);
const handleCloseAllClick = () => {
setIsNavOpen(false);
};
return (
<>
<nav className="border-gray-200 bg-white dark:bg-gray-900">
<div className="mx-auto flex max-w-screen-xl flex-wrap items-center justify-between p-4">
{/* Main Nav Level-1 Section */}
<div>
<ul className=" flex flex-col rounded-lg border border-gray-100 bg-gray-50 p-4 font-medium rtl:space-x-reverse dark:border-gray-700 dark:bg-gray-800 md:mt-0 md:flex-row md:space-x-8 md:border-0 md:bg-white md:p-0 md:dark:bg-gray-900">
{/* Home Link */}
<li>
<Link
href="/"
onClick={handleCloseAllClick}
>
Home
</Link>
</li>
{/* About Link */}
<li>
<Link
href="/about"
onClick={handleCloseAllClick}
>
<span>About</span>
</Link>
</li>
<AuthClientInOutWrapperCatto>
<AuthServerInOutFormsCatto />
</AuthClientInOutWrapperCatto>
</ul>
</div>
</div>
</nav>
</>
);
};
export default Nav;
Edit: thanks for the comment from Ahmed Abdelbaset. I removed the "use server" line at the top of the server component AuthServerInOutForms & then the page renders however the sign in button is not clickable & in the console there is this error:
Uncaught (in promise) Error: Invariant: headers() expects to have requestAsyncStorage, none available.
also fyi: If the server component is placed directly in the main layout it will work as expected. The issue is when the server component is attempted to passed into the client component.
The <AuthServerInOutFormsCatto />
is a Server Component, yet it's being utilized within a client component (<Nav />
), which is not feasible.
The mentioned trick, clarifies that Server Components should only be employed within Server Components. To achieve the desired behavior, you can pass the children prop from a Server Component level.
export const Nav = ({ children }) => {
// ...
<AuthClientInOutWrapperCatto>
{children}
</AuthClientInOutWrapperCatto>
}
Then in the layout.tsx (or wherever the <Nav />
is rendered) pass the <AuthServerInOutFormsCatto />
as a child.
export default function Layout() {
return (
<div>
<Nav>
<AuthServerInOutFormsCatto />
</Nav>
</div>
)
}
You don't need "use server"
at the top of AuthServerInOutForms
file. You should move it to the action itself
<form
action={async () => {
"use server";
await signIn();
}}
>
The "use server"
directive is used for Server Actions not Server Components. When marking a file with use server
, Next.js will treat all exports from this file as Server Actions. This might lead to unexpected behaviors.