Skip to content

Document 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.

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.

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.

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.

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.

TagMatched by
metaThe 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 linkrel, href and hreflang.
Anything elseNot 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.

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.