# `Xenia.Metadata`
[🔗](https://github.com/uzaydurdu/xenia/blob/v0.1.1/lib/xenia/app/metadata.ex#L1)

Page titles, descriptions and social-card tags for an app folder.

A page, and any `layout.ex` above it, can define `metadata/1`. It gets
the page's assigns and returns a map:

```elixir
# app/layout.ex: the defaults for every page
def metadata(_assigns) do
  %{
    title: "Acme",
    title_template: "%s · Acme",
    description: "Acme makes rockets.",
    open_graph: %{site_name: "Acme", type: "website"}
  }
end

# app/posts/[slug]/page.ex
def metadata(assigns) do
  %{title: assigns.post.title, description: assigns.post.summary}
end
```

Xenia merges them from the outermost layout to the page: a key set lower
down wins, and `open_graph` and `twitter` merge key by key. The page's
`title` goes through the nearest `title_template` (`"Hello · Acme"`); a
page without a title gets its layouts' `title` as it is.

The result is assigned as `@xenia_metadata`, and its title as
`@page_title`, so the browser tab follows live navigation like any
`page_title`. It's computed after the page has its data: after `mount/3`,
or after the page's own `handle_params/3` if it has one.

A page that assigns `page_title` itself, and has no metadata `title`,
keeps its `page_title`: a layout's default title only fills in for pages
without one.

Render the tags in `root.html.heex`, inside `<head>`:

```heex
<.live_title default="Acme">{assigns[:page_title]}</.live_title>
<Xenia.Metadata.tags metadata={assigns[:xenia_metadata]} />
```

The root layout is rendered once, on the first page load, which is what
search engines and link previews read. On live navigation LiveView
updates the `<title>`, but not the other tags in `<head>`.

## Keys

  * `:title` - the page title
  * `:title_template` - wraps titles set below this layout; `%s` is the title
  * `:description` - `<meta name="description">`
  * `:canonical` - `<link rel="canonical">`, a full URL
  * `:robots` - `<meta name="robots">`, e.g. `"noindex"`
  * `:open_graph` - `og:` tags, e.g. `%{type: "article", image: "https://..."}`.
    `og:title` and `og:description` default to the title and description
  * `:twitter` - `twitter:` tags, e.g. `%{card: "summary_large_image"}`

Any other key raises an `ArgumentError`, so a typo doesn't go unnoticed.

# `t`

```elixir
@type t() :: %{optional(atom()) =&gt; String.t() | map()}
```

# `resolve`

```elixir
@spec resolve([module()], module() | nil, map()) :: t() | nil
```

Merges the metadata of `layouts` (outermost first) and `page`, called with
`assigns`. Modules without `metadata/1` are skipped. Returns `nil` when
none of them has any.

# `tags`

Renders the `<meta>` and `<link>` tags for resolved metadata. Put it in
`root.html.heex`, inside `<head>`, next to `<.live_title>`, which renders
the title.

## Attributes

* `metadata` (`:map`) - assigns[:xenia_metadata]. Defaults to `nil`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
