Server component nested inside a client component
Unanswered
Ruyy posted this in #help-forum
RuyyOP
I'm working on a project using Next.js 14 and Supabase auth. In my project, I have a Navbar component that's a client-side component. However, one of the items in the Navbar is a 'signout' button that utilizes Supabase Server Actions.
I'm wondering how I can integrate this 'signout' button component within the Navbar.
I'm wondering how I can integrate this 'signout' button component within the Navbar.
15 Replies
@Ruyy I'm working on a project using Next.js 14 and Supabase auth. In my project, I have a Navbar component that's a client-side component. However, one of the items in the Navbar is a 'signout' button that utilizes Supabase Server Actions.
I'm wondering how I can integrate this 'signout' button component within the Navbar.
RuyyOP
SidebarNav.tsx"use client";
// A bunch of imports
import { SignOutButton } from "../auth/SignOutButton";
const SidebarNavItems: ISidebarNavItem[] = [
// All sidebar items
]
export function SidebarNav() {
const [isExpanded, setIsExpanded] = useState(false);
const { theme, setTheme } = useTheme();
return (
<aside className={cn("flex flex-col gap-5 w-80 h-screen sticky top-0 px-4 py-8", isExpanded ? 'w-80' : 'w-16 items-center')}>
<div className="flex flex-col gap-6 px-4" >
{isExpanded ? <Logo /> : <Aperture className="w-7 text-primary shrink-0" />}
<Separator className="w-full" />
</div>
<nav className={cn("flex flex-col gap-4 grow justify-start", isExpanded ? "items-start" : "items-center")}>
{SidebarNavItems.map(({ href, title, icon, disabled, external }) => (
<SidebarNavItem href={href} key={title} title={title} icon={icon} disabled={disabled} external={external} isExpanded={isExpanded} />)
)}
</nav>
<div className={cn("flex flex-col gap-4", isExpanded ? "items-start" : "items-center")}>
// A bunch of buttons
<SignOutButton>
<Button
variant="ghost"
size={isExpanded ? "default" : "icon"}
className={
cn(
"relative text-muted-foreground hover:text-secondary-foreground transition-colors",
isExpanded ? "justify-start flex items-center gap-4 w-full" : "justify-center"
)
}
>
<figure className="shrink-0 w-5 flex items-center justify-center aspect-square">
<LogOut />
</figure>
{isExpanded && <span>Sair da plataforma</span>}
</Button>
</SignOutButton>
</div>
</aside>
);
};@Ruyy I'm working on a project using Next.js 14 and Supabase auth. In my project, I have a Navbar component that's a client-side component. However, one of the items in the Navbar is a 'signout' button that utilizes Supabase Server Actions.
I'm wondering how I can integrate this 'signout' button component within the Navbar.
RuyyOP
SignOutButton.tsximport { createClient } from '@/lib/database/server'
import { cookies } from 'next/headers'
import { redirect } from 'next/navigation'
import { ReactNode } from 'react'
interface SignOutButtonProps {
children?: ReactNode
}
export async function SignOutButton({ children }: SignOutButtonProps) {
const cookieStore = cookies()
const supabase = createClient(cookieStore)
const {
data: { user },
} = await supabase.auth.getUser()
const signOut = async () => {
'use server'
const cookieStore = cookies()
const supabase = createClient(cookieStore)
await supabase.auth.signOut()
return redirect('/auth/login')
}
if (!user) return null;
return (
<form action={signOut}>
{children}
</form>
)
}Pass the signout button to the sidebar through props, either as children or as it's own prop
@Plague Pass the signout button to the sidebar through props, either as children or as it's own prop
RuyyOP
The issue is, the SignOutButton must receive either children or props.
Cuz it has conditionals depending on the Sidebar
isExpanded state <SignOutButton>
<Button
variant="ghost"
size={isExpanded ? "default" : "icon"}
className={
cn(
"relative text-muted-foreground hover:text-secondary-foreground transition-colors",
isExpanded ? "justify-start flex items-center gap-4 w-full" : "justify-center"
)
}
>
<figure className="shrink-0 w-5 flex items-center justify-center aspect-square">
<LogOut />
</figure>
{isExpanded && <span>Sair da plataforma</span>}
</Button>
</SignOutButton>@Ruyy The issue is, the SignOutButton must receive either children or props.
Then pass the button to the sidebar as a prop, render it in the sidebar, pass children to it there, I don't think you'll be able to pass props directly to it though, but, children should work.
Yeah literally exactly what you're doing, but, SignOutButton is a prop instead of an imported component
@Plague Then pass the button to the sidebar as a prop, render it in the sidebar, pass children to it there, I don't think you'll be able to pass props directly to it though, but, children should work.
RuyyOP
I understand what you said, however, cant realy undertand the code... LOL
dashboard/layout.tsx <div className="flex w-full min-h-screen">
<SidebarNav SignOutWrapper={<SignOutButton />} />
<main className="flex-1">
{children}
</main>
</div>`SidebarNav.tsx// a bunch of imports
interface SidebarNavProps {
SignOutWrapper: ReactNode;
};
export function SidebarNav({ SignOutWrapper }: SidebarNavProps) {
// some state functions
return (
<aside {...}>
// a bunch of nav code
<SignOutWrapper>
<Button
variant="ghost"
size={isExpanded ? "default" : "icon"}
className={
cn(
"relative text-muted-foreground hover:text-secondary-foreground transition-colors",
isExpanded ? "justify-start flex items-center gap-4 w-full" : "justify-center"
)
}
>
<figure className="shrink-0 w-5 flex items-center justify-center aspect-square">
<LogOut />
</figure>
{isExpanded && <span>Sair da plataforma</span>}
</Button>
</SignOutWrapper>
</aside>
)
}@Ruyy I understand what you said, however, cant realy undertand the code... LOL
Ah okay, I misunderstood, what I'm talking about is not possible, you can't render the prop as a component so like: <SignOutWrapper /> you can only render it by saying { SignOutWrapper } so it is essentially just like the children prop except it has a unique name so you can seperate it from other children.
In your case then, if you want the children of
I am not seeing another way to get the children of a server component those state values without using context and wrapping them in a client component.
In your case then, if you want the children of
SignOutButton to depend on state from the Sidebar, you're going to have to lift that state up into context, consume it in a client component that wraps your button, then render that client component inside your SignOutButton and then pass the SignOutButton to your sidebar.I am not seeing another way to get the children of a server component those state values without using context and wrapping them in a client component.
This is very common in the app directory for navigation, some parts don't need to be client components or in your case can't, so context for the state is ideal.