Server page being recognized as a Client component

I have a server component src/app/blog/[slug]/page.tsx that is being recognized as a client component and I'm not seeing why. It IS rendering a client component <CommentLayout /> but, as I understand, that shouldn't matter. I'm getting an error fetching data from the file system and generating MetaData for the route. Am I going about this in the wrong manner?
Here's the error I'm getting which states to not export the generateMetadata function but the docs clearly state this is fine.
File path:
./src/app/blog/[slug]/page.tsx
./src/app/blog/[slug]/page.tsx
ReactServerComponentsError:

You are attempting to export "generateMetadata" from a component marked with "use client", which is disallowed. Either remove the export, or the "use client" directive. Read more: https://nextjs.org/docs/getting-started/react-essentials#the-use-client-directive

╭─[/Users/Panzermensch/Desktop/Projects/next-blog/src/app/blog/[slug]/page.tsx:48:1]
48 │ };
49 │ }
50
51 │ export async function generateMetadata({
· ────────────────
52 │ params,
53 │ }: {
54 │ params: { slug: string };
╰────
File path:
./src/app/blog/[slug]/page.tsx
./src/app/blog/[slug]/page.tsx
ReactServerComponentsError:

You are attempting to export "generateMetadata" from a component marked with "use client", which is disallowed. Either remove the export, or the "use client" directive. Read more: https://nextjs.org/docs/getting-started/react-essentials#the-use-client-directive

╭─[/Users/Panzermensch/Desktop/Projects/next-blog/src/app/blog/[slug]/page.tsx:48:1]
48 │ };
49 │ }
50
51 │ export async function generateMetadata({
· ────────────────
52 │ params,
53 │ }: {
54 │ params: { slug: string };
╰────
Functions: generateMetadata
Learn how to add Metadata to your Next.js application for improved search engine optimization (SEO) and web shareability.
12 Replies
Panziewanz
Panziewanz13mo ago
Here's the component for reference.
import Link from 'next/link';
import { type Metadata } from 'next';
import { type NonNullablePostOptions } from '~/interfaces/post';
import { getPostBySlug, getPostSlugs } from 'lib/blogApi';
import markdownToHtml from 'lib/markdownToHtml';
import PostBody from '~/components/postBody';
import readingTime from 'reading-time';
import { CommentLayout } from '~/app/blog/[slug]/commentLayout';

export async function getPost(slug: string) {
// ... calling getPostBySlug which utilizes fs to fetch content in md
}

export async function generateStaticParams() {
// ...
}

export async function generateMetadata({
params,
}: {
params: { slug: string };
}): Promise<Metadata> {
// ...
return {
title: postData.title,
description: postData.excerpt,
};
}

export default async function Post({
params: { slug },
}: {
params: { slug: string };
}) {
const { post, stats } = await getPost(slug);

return (
<>
// ...
<CommentLayout slug={post.slug} />
// ...
</>
);
}
import Link from 'next/link';
import { type Metadata } from 'next';
import { type NonNullablePostOptions } from '~/interfaces/post';
import { getPostBySlug, getPostSlugs } from 'lib/blogApi';
import markdownToHtml from 'lib/markdownToHtml';
import PostBody from '~/components/postBody';
import readingTime from 'reading-time';
import { CommentLayout } from '~/app/blog/[slug]/commentLayout';

export async function getPost(slug: string) {
// ... calling getPostBySlug which utilizes fs to fetch content in md
}

export async function generateStaticParams() {
// ...
}

export async function generateMetadata({
params,
}: {
params: { slug: string };
}): Promise<Metadata> {
// ...
return {
title: postData.title,
description: postData.excerpt,
};
}

export default async function Post({
params: { slug },
}: {
params: { slug: string };
}) {
const { post, stats } = await getPost(slug);

return (
<>
// ...
<CommentLayout slug={post.slug} />
// ...
</>
);
}
alexmartos
alexmartos13mo ago
Is the layout a client component?
Panziewanz
Panziewanz13mo ago
layout:
import '~/styles/globals.css';
import '~/styles/footnote.css';
import { type Metadata } from 'next';
import Layout from '~/app/pageLayout';
import { TRPCReactProvider } from '~/trpc/react';
import { headers } from 'next/headers';

export const metaData: Metadata = {
title: 'title',
description: 'stuff'
};

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<TRPCReactProvider headers={headers()}>
<Layout>{children}</Layout>
</TRPCReactProvider>
</body>
</html>
);
}
import '~/styles/globals.css';
import '~/styles/footnote.css';
import { type Metadata } from 'next';
import Layout from '~/app/pageLayout';
import { TRPCReactProvider } from '~/trpc/react';
import { headers } from 'next/headers';

export const metaData: Metadata = {
title: 'title',
description: 'stuff'
};

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<TRPCReactProvider headers={headers()}>
<Layout>{children}</Layout>
</TRPCReactProvider>
</body>
</html>
);
}
No. The layout is server. I'm attempting to migrate my app from the pages to the app router and running into some strange behavior. I may give it another 2-6 months before attempting this again. I'm having issues with the metadata durring build now as well. I don't know what changed but the title was rendering on the browser tab and no longer is. I'm getting this type error
Type error: Page "src/app/about/page.tsx" does not match the required types of a Next.js Page.
"metaData" is not a valid Page export field.
Type error: Page "src/app/about/page.tsx" does not match the required types of a Next.js Page.
"metaData" is not a valid Page export field.
the metadata is all stuctured as above.
alexmartos
alexmartos13mo ago
What version of next do you have?
Panziewanz
Panziewanz13mo ago
"next": "^13.5.5"
Max
Max13mo ago
maybe try deleting your .next directory and re running dev/build? is the first file your page? This is what i would do... about/layout and about/page. and root layout would be src/app/layout. and see if that helps
Panziewanz
Panziewanz13mo ago
I deleted the .next dir and ran build again, heres the error:
Creating an optimized production build
Linting and checking validity of types ...Failed to compile.

src/app/about/page.tsx
Type error: Page "src/app/about/page.tsx" does not match the required types of a Next.js Page.
"metaData" is not a valid Page export field.
Linting and checking validity of types .%
Creating an optimized production build
Linting and checking validity of types ...Failed to compile.

src/app/about/page.tsx
Type error: Page "src/app/about/page.tsx" does not match the required types of a Next.js Page.
"metaData" is not a valid Page export field.
Linting and checking validity of types .%
The first file is the page, yes. I'm sorry I dont' follow your suggestion, "This is what i would do... about/layout and about/page. and root layout would be src/app/layout. and see if that helps"
Max
Max13mo ago
Can you share a github link to this repo?
Panziewanz
Panziewanz13mo ago
GitHub
GitHub - L-Steinmacher/next-blog at app-router
making a blog with T3 stack and Next.js. Contribute to L-Steinmacher/next-blog development by creating an account on GitHub.
Max
Max13mo ago
Okay so change metaData to metadata and don't export your getBlogPosts in app/page and let me know if that works. do a search all cause you're doing it multiple places.
Panziewanz
Panziewanz13mo ago
Holy F***. I feel so dumn, thank you.
Max
Max13mo ago
i'd also move import Layout from '~/app/pageLayout' from root layout. so create a about/layout and import it there or just combine it all in your root layout app/layout. Just a sugestion tho. Yeah no problem, I've made plenty of mistakes coding is hard. Feel free t mark my answer as correct 🙂
Want results from more Discord servers?
Add your server