Need help with server/client components
Unanswered
Black imported fire ant posted this in #help-forum
Black imported fire antOP
Hey Everyone, I hope you're all doing well
I'm fairly new to web development in general and to nextjs more specifically, and for our university final project, I started developing a relatively simple web app that contains a few databases (using supabase) and visualizing them in tables (from shadcn/ui)
I have two main issues with the project that I still can't find how to solve:
- First issue is that I have a component called "UserCard" inside of all my pages (in the header) which has a profile pic, name of the profile and the email, to get this info I'm doing a fetch, which has turned this component into a server component and that causes issues (since it's a server component inside a client component), how can I fix it?
I'm fairly new to web development in general and to nextjs more specifically, and for our university final project, I started developing a relatively simple web app that contains a few databases (using supabase) and visualizing them in tables (from shadcn/ui)
I have two main issues with the project that I still can't find how to solve:
- First issue is that I have a component called "UserCard" inside of all my pages (in the header) which has a profile pic, name of the profile and the email, to get this info I'm doing a fetch, which has turned this component into a server component and that causes issues (since it's a server component inside a client component), how can I fix it?
28 Replies
Black imported fire antOP
UserCard.tsx
import React from 'react'
import Link from 'next/link'
import { createClient } from "@/src/utils/supabase/server";
import { Bell, Bolt, LogOut, Moon, User, Wallet } from 'lucide-react'
import { Avatar, AvatarFallback, AvatarImage, } from "@/src/components/ui/avatar"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger} from "@/src/components/ui/dropdown-menu"
import { Button } from './ui/button';
import signOut from '@/src/lib/signOut';
import { redirect } from "next/navigation";export default async function UserCard () {
const supabase = createClient();
const {
data: { user },
} = await supabase.auth.getUser();
const {
data: collaborator,
error
} = await supabase.from("collaborators").select("*").eq("user", user?.id || "").single();
if (!collaborator) {
redirect("/profile");
} return user && collaborator && !error ? (
<div className='flex items-center'>
<DropdownMenu>
<DropdownMenuTrigger>
<Link href={"/client"} className='flex p-2 rounded-lg hover:bg-gray-200'>
<Avatar className='mr-3 size-12'>
<AvatarImage src="https://github.com/shadcn.png" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<div className='flex flex-col items-start justify-center'>
<span className='font-bold'>{collaborator!.full_name}</span>
<span className='text-xs text-[#6A6A6A] leading-none'>{user.email}</span>
</div>
</Link>
</DropdownMenuTrigger>
<DropdownMenuContent className="bg-white w-44">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator className="bg-gray-200" />
<DropdownMenuItem className="">
<User className="mr-2 size-4" />
<span>Profile</span>
</DropdownMenuItem>
<DropdownMenuItem className="">
<Wallet className="mr-2 size-4" />
<span>Billing</span>
</DropdownMenuItem>
<DropdownMenuItem className="">
<Bolt className="mr-2 size-4" />
<span>Settings</span>
</DropdownMenuItem>
<DropdownMenuItem className="focus:text-white focus:bg-red-500" >
<div>
<LogOut className="mr-2 size-4" />
</div>
<form action={signOut} className="w-full h-full">
<button className="no-underline w-full h-full text-left ">
Logout
</button>
</form>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div> ) : (
<div className='flex items-center'>
<Moon fill="#7E7E7E" strokeWidth={0} className='mr-4 size-7' />
<Bell fill="#7E7E7E" strokeWidth={0} className='mr-5 size-7' />
<Button asChild>
<Link href="/login">Log In</Link>
</Button>
</div>
)
}- The second issue is somewhat similar to the first, in my "projects" table, I have a "Client" column, that uses a "clientBadge" component, since the projects table only contains the client id, the component has a function that fetches the name of the client based off his ID, I tried to seperate the fetch logic from the component but I still have that issue
ClientBadge.tsx:
ClientBadge.tsx:
import React, { FC } from 'react'
import { Badge } from '../ui/badge';
import { getClientName } from './actions';
interface ClientBadgeProps {
clientID: string | null;
}
const ClientBadge: FC <ClientBadgeProps> = ({clientID}) => {
const clientName = getClientName(clientID);
return clientName ? (
<div>
<Badge variant="secondary">{clientName}</Badge>
</div>
) : (
<div>-</div>
)
}
export default ClientBadgeactions.ts:
I would really appreciate any help, especially
import { createClient } from "@/src/utils/supabase/client";
export const getClientName = async (clientID : string | null) => {
const supabase = createClient();
if (clientID != null) {
const {
data: client,
error
} = await supabase.from("clients").select("*").eq("id", clientID || "").single();
return client.name
}
return "-"
}I would really appreciate any help, especially
Netherland Dwarf
You can still use server component inside client
But you will have to put “use server”
Inside the server component
wrong. that's server actions, not server components
Netherland Dwarf
@joulev sorry, hm i guess i need to clarify my understanding because , but almost every where all the devs are saying that even on the official vercel channel
From what i understand so far, importing server component into a client component will convert the server comp into a client, to prevent this we need add “use server” on the server component that is being imported into the client
Server actions are not server components
@Netherland Dwarf From what i understand so far, importing server component into a client component will convert the server comp into a client, to prevent this we need add “use server” on the server component that is being imported into the client
The part before the comma is correct. The part after it is not correct. The correct, and the only, way to “nest” a server component to a client component is by the interleave method linked above, where you [send the client component the “output” of the server component](https://nextjs-faq.com/client-components-wrapping-server-component-children)
Netherland Dwarf
Sure np il look for it
Black imported fire antOP
I had a friend help me both issues
thank you @joulev
I had another issue with shadcn's forms
basically, had a input where I'm supposed to edit a number
but it gives this error
@Netherland Dwarfany clue on how I can convert the string to a number before it's submitted?
@Black imported fire ant I had another issue with shadcn's forms
Great black wasp
@Black imported fire ant every input returns a string even if you use type="number" to an input, so if you're like using zod switching to z.coerce.number() works fine
Black imported fire antOP
I ended up using a different command in the schema
revenue: z.preprocess((val) => Number(val), z.number().optional()),Madeiran sardinella
Hi, just for understanding, you are using that data and that component throughout the app right? Why don't you move them to a layout and pass the data by props?