# Document head

Source: https://signy.dev/documentation/1/application/head/

The title and tags of the page, set from a component.

`useHead()` sets `document.title`, `<meta>` and `<link>` tags for as long as the current owner lives. Call it in `setup()`. When the component unmounts, the page goes back to what it was.

```js
class Inbox extends Component {
  setup() {
    useHead({
      title: 'Inbox',
      meta: [{ name: 'description', content: 'Your messages' }],
      link: [{ rel: 'canonical', href: '/inbox' }],
    });
  }
}
```

Each tag is a plain object of attributes. A value of `undefined` or `null` leaves that attribute out.

## Following signals

Pass a function and it re-runs when a signal it read changes. Only what changed is written to the page.

```js
const { Component, Signal, application, html, useHead, Show } = Signy;

class Promo extends Component {
  title = new Signal("Sale starts now");

  setup() {
    useHead(() => ({
      title: this.title.value,
      meta: [{ name: "description", content: this.title.value }],
    }));
  }

  render() {
    return html`<input aria-label="Tab title" bind:value=${this.title} />`;
  }
}

class Demo extends Component {
  open = new Signal(false);

  render() {
    return html`
      <button onClick=${() => this.open.set((o) => !o)}>
        ${() => (this.open.value ? "Remove the head" : "Set the head")}
      </button>
      ${Show({ when: () => this.open.value, children: () => html`<${Promo} />` })}
      <p>Watch the title of this tab.</p>
    `;
  }
}

application(Demo).mount("#head-demo");
```

The button builds the child through a function, ``children: () => html`...```, so the head entry belongs to that branch and goes when it is removed. A child written directly between the `Show` tags is built when the parent renders, and its `useHead()` would stay.

## Layouts and pages

Calls stack. The latest entry wins for the title and for each tag, and when it is removed the one before it comes back. A layout sets defaults, a page overrides them, and leaving the page restores the layout's values.

```js
class Layout extends Component {
  setup() {
    useHead({ titleTemplate: '%s - Signy',
      meta: [{ name: 'description', content: 'A UI framework' }] });
  }
}

class Pricing extends Component {
  setup() {
    useHead({ title: 'Pricing', meta: [{ name: 'description', content: 'Plans' }] });
  }
}
// On the pricing page: "Pricing - Signy", description "Plans".
// After leaving it: the title from your HTML file, description "A UI framework".
```

## Title template

`titleTemplate` wraps the winning title. It is a string with `%s`, or a function. It can come from a different entry than the title, so a layout can set it once. A template alone does nothing: there has to be a title.

```js
useHead({ titleTemplate: (title) => `${title} | Signy` });
```

## Which tag replaces which

Two entries that describe the same tag share it. Anything without a key is added once per call.

| Tag | Matched by |
| --- | --- |
| `meta` | The first of `name`, `property`, `http-equiv`, `itemprop` or `charset` that it has, and its value. |
| `link` with `rel="canonical"` or `rel="manifest"` | `rel` alone, so there is only one. |
| Other `link` | `rel`, `href` and `hreflang`. |
| Anything else | Not matched. Each call adds its own tag. |

A tag that is already in the page, such as the description in your HTML file, is taken over while an entry covers it and restored afterward. Tags that `useHead()` adds carry `data-signy-head` and are removed when the entry goes.

## With the router

A route component runs `setup()` when the route is entered and unmounts when it is left, so the stacking above gives every route its own title with no extra code. A title from the route's data works the same way.

```js
class UserPage extends Component {
  setup() {
    const params = useParams(this);
    useHead(() => ({ title: `User ${params.value.id}` }));
  }
}
```

## Safety

Attributes go through the same check as bindings. A `javascript:` URL in `href`, or an `on*` attribute, is not written, and a development build logs a warning.

## Limits

- It runs in the browser. A crawler or a link preview that does not run scripts sees the tags in your HTML file, not the ones set here.
- Outside an owner, such as top level code, nothing removes the entries. Call it in `setup()` or inside `createRoot()`.
- `<html>` and `<body>` attributes, such as `lang`, are not managed.
