Issue with 'use client' Directive and SSR in Next.js: Conflicting Errors and Resolution"

Hello, this code reproduces the error i'm facing "use client"; import { useState } from "react"; import { api } from "~/trpc/server"; export default function Text() { const [isSignUp, setIsSignUp] = useState(false); const handleSignUp = async () => { try { const { name, email, password } = { name: "John Doe", email: "[email protected]", password: "password123" }; const signupReq = await api.user.signup.mutate({ name, email, password }); console.log(signupReq); } catch (error) { console.error("Error occurred during sign up:", error); } }; return ( <main className=""> <button onClick={handleSignUp}>Sign Up</button> </main> ); } When I remove the 'use client' directive, I receive the following error: 'Maybe one of these should be marked as a client entry with "use client".' However, when I leave the 'use client' directive in place, I encounter the error: 'You're importing a component that needs next/headers. That only works in a Server Component but one of its parents is marked with "use client", so it's a Client Component.' I'm unsure how to proceed in this situation. Any guidance on resolving this issue would be greatly appreciated.
14 Replies
ATOMowy_grzyb
ATOMowy_grzyb14mo ago
separate your concerns, you're mixing client side code with server side code in one component, you can't do that create an API route with a route handler that will handle your api.user.signup.mutate and call that over HTTP from the client component, or wrap the button in a form and use a server action on the form you have "use client" because you have useState but you don't use the state for anything so it makes no sense
G.A.S
G.A.SOP14mo ago
the code earlier was just something to reproduce the error. import React, { useState } from "react"; import { api } from "~/trpc/server"; export const AuthForm = () => { const [isSignUp, setIsSignUp] = useState(false); const handleAuth = async (e) => { e.preventDefault(); const formData = new FormData(e.currentTarget as HTMLFormElement); const email = formData.get("email") as string; const password = formData.get("password") as string; let name = ""; if (isSignUp) { name = formData.get("name") as string; } if (isSignUp) { // Sign up logic const signupReq = await api.user.signup.mutate({ name, email, password, }); console.log(signupReq); } else { // Sign in logic console.log(Perform sign in logic here ${email} ${password}); } }; const toggleAuthMode = () => { setIsSignUp((prev) => !prev); }; return ( <div> <button type="button" onClick={toggleAuthMode}> {isSignUp ? "Sign In" : "Sign Up"} </button> <form onSubmit={handleAuth}> {isSignUp && <input type="text" name="name" placeholder="Name" />} <input type="email" name="email" placeholder="Work Email" /> <input type="password" name="password" placeholder="Password" /> <button type="submit">Submit</button> </form> </div> ); }; This is the filtered code in question. While reviewing various examples, I found instances where the use of the "use client" directive and the API import in the same component appeared to work seamlessly , so i was thinking maybe i seemed to be doing something wrong
sincere666
sincere66614mo ago
can you show the code inside /trpc/server?
G.A.S
G.A.SOP14mo ago
import { createTRPCProxyClient, loggerLink, unstable_httpBatchStreamLink, } from "@trpc/client"; import { headers } from "next/headers"; import { type AppRouter } from "~/server/api/root"; import { getUrl, transformer } from "./shared"; export const api = createTRPCProxyClient<AppRouter>({ transformer, links: [ loggerLink({ enabled: (op) => process.env.NODE_ENV === "development" || (op.direction === "down" && op.result instanceof Error), }), unstable_httpBatchStreamLink({ url: getUrl(), headers() { const heads = new Map(headers()); heads.set("x-trpc-source", "rsc"); return Object.fromEntries(heads); }, }), ], });
sincere666
sincere66614mo ago
So the problem is next/headers should be used inside a "server component"
beroer
beroer14mo ago
i have something similar 1 post above, where i'm trying to render a server component as children of a client component whose ancestors are all server components and i'm getting the same error.
sincere666
sincere66614mo ago
I think you have no other way except to extract the code to an /api route and call that route with fetch inside this client-side AuthForm component you can make two routes like : /api/signin and /api/signup to implement both methods
G.A.S
G.A.SOP14mo ago
Yeah that was the same suggestion i got earlier , but doesn't that combat the whole purpose of using trpc ? or no ?
sincere666
sincere66614mo ago
Actually it is, but since the new /app dir update, trpc hasn't yet figured out the new solution for RSC That's why many people still prefer the /pages version have you tried to pass the children like this? : <ClientLayout> {children} </ClientLayout>
G.A.S
G.A.SOP14mo ago
oh okay. I'll try the Next.js pages router for now and maybe come back to tRPC later when it works smoothly with the app router. It seems tricky to not use both server and client components in the same parent component. The new app router wants a clear separation between server and client components, so I might need to adjust my current approach.
beroer
beroer14mo ago
thanks, turns out it was due to a barrel import importing client and server components as well in the same index.ts file. separating the index to client and server imports solved it.
sincere666
sincere66614mo ago
Okay no probs, how you export the files in index.ts? Is it like export * from '...'?
sincere666
sincere66614mo ago
If you meant to export a client or server component from an index.ts, you can do it actually, here's the complete example with your cases where parent components are server so it's like server > client > server
No description
No description
No description
No description
No description
beroer
beroer14mo ago
like this:

export { default as Button } from './Button'

export { default as Button } from './Button'
yeah, i had client and server components conflicting within the same index.js and i was using a supabase client in a server component which was giving me problems. it's been resolved now by separating client and server, but i appreciate the help! it was that actually, not the composition.
Want results from more Discord servers?
Add your server