Next.js Discord

Discord Forum

Best way to set titles in app router with lots of pages?

Answered
Sun bear posted this in #help-forum
Open in Discord
Avatar
Sun bearOP
Like <domain> | <page>
example.com | login for example.
Answered by B33fb0n3
that depends on how you structure your project. Some examples on how you can structure your project are here: https://nextjs.org/docs/app/building-your-application/routing/colocation#project-organization-strategies

I guess minimum one of the suites your case
View full answer

28 Replies

Avatar
you can use a template inside your title or description or ... to put everything together. Example:
export const metadata: Metadata = {
    title: {
        default: "I am some title", // this will be used as title on the page where you add this metadata
        template: "%s | Example.com", // the %s will be exchanged with the new title, when you set a new title someone on any child
    },
}
Avatar
Sun bearOP
How would I do it from the page file where its marked as 'use client'?
Avatar
you can only set the metadata inside server component. However you can do a little trick and set the document.title to your desired title
Avatar
Sun bearOP
is there any better ways?
Avatar
what's your use case? What do you want to do?
Avatar
Sun bearOP
Literally just add custom titles to indicate what page the user is one, but alot of my pages are marked as 'use client' due to the states and everything involved
Avatar
the page.tsx itself is by default always a server component in app router. So keep this serverside especially for that kind of stuff and only import your client components into it. Like that most of your page will be rendered serverside and only the parts that need to be client components are client components
Avatar
Sun bearOP
what file structure should I use for this then?
Avatar
you would use a page.tsx as server component and inside this you would import your client components. It could look like this:
// page.tsx

// add some metadata

export default function Page() {
  // serverside
  const post = await getLastBlockPost()

  return <>
  <h1>My Blog Page</h1>
  <BlogPost post={post}/> // serverside
  <CommentSection initialComments={post.comments} postId={post.id}/> // clientside
</>
}
Avatar
Sun bearOP
I understand that part, I'm asking how I would structure the actual files in my project directory
Avatar
create a page.tsx and create a YourClientComponent.tsx as your client component file. Nothing more is needed for what I am talking about
Avatar
Sun bearOP
in the same folder?
Avatar
that depends on how you structure your project. Some examples on how you can structure your project are here: https://nextjs.org/docs/app/building-your-application/routing/colocation#project-organization-strategies

I guess minimum one of the suites your case
Answer
Avatar
Indian oil sardine
i'm using something like this

app/
├── (routes)
│   └── example-page/
│       ├── page.tsx
│       └── page.client.tsx

page.tsx is the server component where i set metadata

import type { Metadata } from "next";
import { getUser } from "@/_data/user";
import { getEmployeeDirectory } from "./_lib/employee-directory.queries";
import {
  dehydrate,
  HydrationBoundary,
  QueryClient,
} from "@tanstack/react-query";
import EmployeeDirectoryClient from "./page.client";

export const metadata: Metadata = {
  title: "Employee Directory | Your Site",
};

export default async function EmployeeDirectoryPage() {
  const user = await getUser();

  const queryClient = new QueryClient();
  await queryClient.prefetchQuery({
    queryKey: ["employees"],
    queryFn: () => getEmployeeDirectory(),
  });

  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <EmployeeDirectoryClient user={user} />
    </HydrationBoundary>
  );
}


page.client.tsx is the client component

"use client";

import { Suspense } from "react";
import { Heading } from "@/components/catalyst/heading";
import { DataTable } from "@/components/data-table/data-table";
import { columns } from "./columns";
import { useQuery } from "@tanstack/react-query";
import { getEmployeeDirectory } from "./_lib/employee-directory.queries";
import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton";
import { UpdateEmployeeDirectory } from "./_components/update-employee-directory";

export default function EmployeeDirectoryClient({ user }: { user: User }) {
    const { data: employees } = useQuery({
      queryKey: ["employees"],
      queryFn: () => getEmployeeDirectory(),
    });
  return (
    <div>
      <div>
        <Heading>Employee Directory</Heading>
      </div>
      <Suspense fallback={<DataTableSkeleton />}>
         <DataTable
            data={employees}
            columns={columns}
          />
      </Suspense>
    </div>
  );
}
Image
Avatar
@Sun bear solved?
Avatar
Sun bearOP
Not yet, my data is fetched from a seperate Express backend and I'm unsure how I would do it with that?
Avatar
you can either fetch it inside your page.tsx and set it thought the generateMetadata function or fetch your express app clientside and set it via document.title both ways are technically possible
Avatar
Sun bearOP
Does generateMetadata work in a client component?
Avatar
no, it's also only for server components. You can also do a combination: fetch your epxress server, serverside and pass it down via props and set the title to the value from the props
Avatar
Sun bearOP
Okay, say I want to fetch data in one page, and reuse that response across all my other components, and I need the request to be made in the client, how would I go about doing this when switching to SSR
If that makes sense
Avatar
What you are saying makes no sense:

you saying that you want to fetch it on the client
request to be made in the client
But on the other hand you want to use SSR (Serverside rendered)
switching to SSR

The best flow would be to fetch everything serverside and pass the data to your components that needs them. Like that you can use SSR and also get rid of all the other problems of setting metadata, titles, …
Avatar
Sun bearOP
Nevermind on that, got it mostly sorted out. One question though, I've noticed that when switching to private folders (like done in the images you sent above) for better organisation, I get the following error when trying to render any icons, I'll attach the error & next config below
/** @type {import('next').NextConfig} */
const nextConfig = {
    webpack(config) {
        config.module.rules.push({
            test: /\.svg$/i,
            issuer: /\.[jt]sx?$/,
            use: ['@svgr/webpack'],
        });

        return config;
    },
};

export default nextConfig;
Image
Avatar
Which message solved the initial question of this thread?
Avatar
Sun bearOP
Sorting using private folders basically
But also raised new issues I need help fixing
Avatar
your new topic looks like a whole different topic. Please create another thread to be able to find solutions. If I marked the wrong message as solution, feel free to mark another one
Avatar
Sun bearOP
Done, #Webpack SVGR not working in certain directories