The title and description are the only things most people ever read about your page — they are the whole result in Google. Here is exactly where they go in Next.js, why they sometimes never reach the HTML, and prompts you can hand to v0 or Cursor to fix a whole site at once.
Look in your project. An app/ folder means the App Router;
a pages/ folder means the Pages Router. Anything v0 built
in the last couple of years is App Router.
Export a metadata object from the page (or layout) file. Next.js renders it into the <head> on the server. A title in a layout applies to every page under it until a page overrides it.
export const metadata = {
title: 'Your page title — under 60 characters',
description: 'One sentence that says what the page is and why to click. Under 155 characters.',
};
export default function Page() {
return <main>…</main>;
} Use next/head. It still ends up in the served HTML, because the page itself is rendered on the server.
import Head from 'next/head';
export default function About() {
return (
<>
<Head>
<title>Your page title — under 60 characters</title>
<meta name="description" content="One sentence that says what the page is and why to click." />
</Head>
<main>…</main>
</>
);
} Doing this page by page is tedious and easy to abandon halfway. Hand one of these to the tool that built your site instead.
Every page on this site is missing a proper title and meta description, so Google shows the wrong text in search results.
For each page in the app/ directory, add a server-rendered metadata export:
export const metadata = {
title: '<a specific title for THIS page, under 60 characters>',
description: '<one sentence saying what the page offers and why to click, under 155 characters>',
};
Rules:
- Every page gets its own title. Do not reuse one title across pages.
- Write what the page actually contains — no keyword stuffing, no "Home | Site | Best".
- Do NOT put the metadata export in a "use client" file; move the page to a server component and keep interactive parts in a child client component.
- Also set metadataBase and a default title template in app/layout.tsx.
When you're done, list every file you changed and the title you gave each page. Audit this Next.js project's metadata and fix it. 1. Find every route that renders a page and check whether it exports metadata (App Router) or renders <Head> (Pages Router). 2. For each page missing a title or description, add one written from the page's actual content: title under 60 characters, description under 155, unique per page. 3. Flag any page that is a "use client" component and therefore cannot export metadata — refactor it so the route file is a server component that renders the client part as a child. 4. In app/layout.tsx set metadataBase to the production URL and a title template. Show me a table of route, old title, new title before you change anything.
My dynamic routes all share one hard-coded title. Give each item its own.
In the App Router route that renders these items, add generateMetadata:
export async function generateMetadata({ params }) {
const item = await getItem(params.slug);
return {
title: item.title,
description: item.summary?.slice(0, 150),
};
}
Use the real fields from my data source, and fall back to something sensible if an item has no summary. Keep it server-side so it appears in the HTML response. Four causes account for nearly every case, and all four look fine in the browser.
"use client" at the top of the page file
A client component cannot export metadata — Next.js silently ignores it, and you get the default title on every page. Make the route file a server component and move the interactive part into a child component that carries the "use client" directive.
Setting the title with useEffect or document.title
That runs after the page loads, in the browser. Google may pick it up eventually, but the HTML response — the thing you see in View Page Source, and the thing crawlers and answer engines read first — still carries the old title. Render it on the server.
One title for the whole site
A layout-level title applies everywhere unless a page overrides it. If every result in Google says the same thing, nobody can tell your pages apart and your click rate suffers on all of them.
Missing metadataBase
Without it, Open Graph and Twitter image paths resolve wrongly and your links share with a broken preview. Set it once in the root layout to your production URL.
OptiSearch reads your Search Console data and names the pages where a better title would earn real clicks — with the rewritten title ready to paste. It then measures whether it worked.
Open the deployed page, press Ctrl+U (or Cmd+Option+U) for View Page Source — not inspect element, which shows the page after JavaScript has run — and search for <title>. If the right title is there, Google sees it. Inspect element can show a title that no crawler ever receives.
Aim for under 60 characters for the title and under 155 for the description. Google truncates beyond roughly that, and it measures pixels rather than characters, so treat both as guidance rather than a hard limit. Front-load the words that matter.
No. Google rewrites descriptions often, especially when the query matches text further down the page. Write it anyway: it is your best offer, it is used most of the time for branded and direct searches, and it is what gets copied into social and AI-generated summaries.
No. Google has ignored the keywords tag for well over a decade. Anything telling you to fill it in is out of date.
It is usually part of it. v0 produces genuinely good UI and leaves the metadata layer empty, so pages ship with a placeholder title or none at all. Metadata alone will not make you rank, but without it Google has to guess what each page is, and it guesses badly.