---
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 ``
or an inline `