--- slug: metadata title: Metadata description: How title, description, and Open Graph tags resolve across layouts and pages. category: Routing and Navigation order: 45 --- Waku renders metadata as ordinary JSX. React hoists `title`, `meta`, and `link` elements into the document head, so a page declares them wherever it likes: ```tsx export default function AboutPage() { return (
About

About

); } ``` ## Pages override layouts React does not deduplicate hoisted tags, so a layout and a page that both declare a `title` would otherwise emit two of them. Waku resolves this while streaming the HTML: when the same key is declared more than once in the head, the **last** declaration wins. A layout that declares its metadata before it renders `{children}` comes first, so the page's declaration comes last. Defaults in a layout, overridden per page, work as expected: ```tsx // src/pages/blog/_layout.tsx export default function BlogLayout({ children }: { children: ReactNode }) { return (
Blog {children}
); } // src/pages/blog/hello.tsx: this page's title wins, og:site_name is inherited export default function HelloPage() { return (
Hello

Hello

); } ``` The keys that are merged are `title`, `meta[name="description"]`, and the Open Graph properties that hold a single value: `og:title`, `og:type`, `og:url`, `og:description`, `og:determiner`, `og:site_name` and `og:locale`. Each is merged on its own, so a layout can set `og:site_name` while a page sets `og:title`. A `name` is matched in any ASCII case, and a `property` exactly as written. This matters most for consumers that do not run JavaScript. Search engine and social-card crawlers read the served HTML, where without merging the layout's tags would come first and win. ## Everything else is your responsibility Tags outside that set are emitted as written, and declaring one twice produces two tags. Most notably: - **`viewport` and `charset`** are not merged. React re-adds on hydration any tag missing from the served HTML, appending it after the survivor, which would flip which `viewport` applies; `charset` is resolved by its _first_ occurrence, so a later one never takes effect anyway. To change either, replace the root component rather than declaring a second tag, as shown below. - **Repeated Open Graph properties** such as `og:image`, `og:video` and `og:locale:alternate` are arrays in OGP, and their order carries meaning, so they are emitted as written. A layout that declares `og:image` is not replaced by a page that declares one. - **Slices** declare their metadata wherever the `` is rendered, so whether a slice or the page wins depends on which comes first. Keep metadata out of reusable slices. - **Metadata inside a `` boundary** is emitted after the head has been flushed and cannot be merged into it. An `async` page component is fine. Only an explicit boundary between the page and its metadata defers it. - **A `` that names itself twice**, such as with both `name` and `property`, turns the merge off for every page it is on. Give each name a tag of its own. - **A head holding anything but metadata** is served as rendered. A `