Next.js Discord

Discord Forum

Beginner help with Server and Client Components

Answered
Brown bear posted this in #help-forum
Open in Discord
Brown bearOP
Hi, I am having issues with getting this to work. I have a callback on a div for onScroll events. This is within a component of its own with the use client directive.

This is implemented in the top level page.tsx.

I am getting the error in the picture attached.

Here is my code:
Answered by Cinnamon Teal
Yeah, the reason for that error is that you are importing some code that's being exported from the server component into a client component. Which you shouldn't really do.

That makes that entire layout.tsx module to be included in the client bundle and also makes Next.js treat it as a client component (it's not 100% a RSC either, you can't use something like useState). So that's why you are getting an error saying you can't export metadata from a client component even though you didn't explicitly used the use client directive in the layout.tsx file.

If you want to import the fonts into separate files like you are doing currently, what you can do is import the font initially from next/fonts into a separate file called fonts.ts or something like that. And then export the fonts from there and import them into each file you want. layout.tsx, HomeScrollSelection.tsx etc.

Or as mentioned above only import them in the layout.tsx and use them directly in the body CSS.
View full answer

9 Replies

Brown bearOP
//layout.tsx

import type { Metadata } from "next";
import { Geist, Finger_Paint, Gloria_Hallelujah } from "next/font/google";
import "./globals.css";

export const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

export const fingerPaint = Finger_Paint({
  weight: '400',
  variable: "--font-finger-paint",
  subsets: ["latin"],
});

export const gloriaHallelujah = Gloria_Hallelujah({
  weight: '400',
  variable: "--font-gloria-hallelujah",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Cai Xuan's Portfolio",
  description: "Cai Xuan (Exrion)'s portfolio/personal website!",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html
      lang="en"
      className={`h-full antialiased`}
    >
      <body className="min-h-full flex flex-col">
        {children}
      </body>
    </html>
  );
}
//page.tsx

import { fingerPaint, geistSans, gloriaHallelujah } from "./layout";
import HomeScrollSelection from './homeScrollSelection';

export default function Home() {
  return (
    <div className={`flex flex-col flex-1 items-center justify-between ${geistSans.className} font-sans pl-6 pr-6 pt-16 pb-16  text-red-600 caret-transparent`}>
      <div></div>
      <div className={`flex flex-col items-center gap-4 sm:gap-12 md:gap-16 mt-35`}>
        <div className={`flex flex-row flex-1 justify-start sm:justify-center w-[380px] sm:w-full text-[#101010]`}>
          <h1 className={`${fingerPaint.className} text-5xl sm:text-center leading-20 whitespace-pre-line md:whitespace-normal`}>
            Welcome to
            Cai Xuan's
            Portfolio
          </h1>
        </div>
        <div className={`flex flex-row flex-1 items-center justify-center gap-1.5 sm:gap-3 md:gap-4 w-full text-[#404040]`}>
          <h2 className={`${gloriaHallelujah.className} text-3xl text-nowrap`}>
            Let's visit
          </h2>
          <div className={`flex flex-row items-center justify-center gap-2 sm:gap-2.5 md:gap-3`}>
            <h2 className={`${gloriaHallelujah.className} text-[64px]`}>
              &#123;
            </h2>
            <HomeScrollSelection/>
            <h2 className={`${gloriaHallelujah.className} text-[64px]`}>
              &#125;
            </h2>
          </div>
        </div>
      </div>
      <div>
        <button>
          CONTINUE
        </button>
      </div>
      <pre></pre>
    </div>
  );
}
//homeScrollSelection.tx

'use client'
import { gloriaHallelujah } from "./layout";

export default function HomeScrollSelection() {
    return (
        <div
            className={`flex flex-col flex-1 items-center justify-start pt-8 pb-8 ${gloriaHallelujah.className} mask-y-from-60% mask-y-to-100% text-3xl h-25 gap-3 text-[#202020] overflow-auto no-scrollbar scroll-smooth snap-y snap-mandatory`}
            onScroll={() => { console.log("scroll"); }}
        >
            <div className={`snap-center snap-always`}>
                <span id="about">
                    <p>
                        About
                    </p>
                </span>
            </div>
            <div className={`snap-center snap-always`}>
                <span id="gallery">
                    <p>
                        Gallery
                    </p>
                </span>
            </div>
            <div className={`snap-center snap-always`}>
                <span id="showcases">
                    <p>
                        Showcases
                    </p>
                </span>
            </div>
        </div>
    );
}
I have followed instructions from various sites to refactor my components into individual files as seen in the snippets above to ensure that client and server components do not conflict, however I still have an issue with the server and client components. Where it claims I am exporting metaddata with a component makred with useclient, yet I never markedd the file with the ddirective.
Transvaal lion
Do not import the font from layout.
@Transvaal lion Do not import the font from layout.
Brown bearOP
Where would I import it?
Transvaal lion
In layout, inject the variables

<body className={${geistSans.variable} ${fingerPaint.variable} ${gloriaHallelujah.variable} min-h-full flex flex-col}>

Then in tailwind config map those those variable to different fonts. After that you can use those fonts.
Cinnamon Teal
Yeah, the reason for that error is that you are importing some code that's being exported from the server component into a client component. Which you shouldn't really do.

That makes that entire layout.tsx module to be included in the client bundle and also makes Next.js treat it as a client component (it's not 100% a RSC either, you can't use something like useState). So that's why you are getting an error saying you can't export metadata from a client component even though you didn't explicitly used the use client directive in the layout.tsx file.

If you want to import the fonts into separate files like you are doing currently, what you can do is import the font initially from next/fonts into a separate file called fonts.ts or something like that. And then export the fonts from there and import them into each file you want. layout.tsx, HomeScrollSelection.tsx etc.

Or as mentioned above only import them in the layout.tsx and use them directly in the body CSS.
Answer
Brown bearOP
I see, thanks a lot yall! The explanation was really handy too. 😄