Head and metadata
import { Head } from "@ramonda/core";
export class StateGuide extends Component {
head = this.use(Head, {
title: "State — Ramonda",
description: "How @state turns a class field into a signal.",
});
render() {
return <article>…</article>;
}
}
renderPage returns them alongside the body:
const page = await renderPage(<App />);
// { body, title, head }
Why a hook
Because the component that knows the title is the leaf, not the shell.
A decorator would fix the value at class-definition time, so a title could never contain anything
the page computed. An option on renderToString would only let the top of the tree speak.
As a hook it composes the way the tree does: a layout sets a default, a route inside it overrides the title, and the deeper one wins.
Options
title |
the <title> |
description |
<meta name="description"> — the snippet under the title in a result |
meta |
anything else: Open Graph, Twitter cards, robots, viewport |
link |
canonical URLs, alternates, icons, preloads |
title and description are first class because they are the two that decide whether anyone
clicks. Set them on every page. A site whose pages all carry one title competes with itself.
Reactive
head = this.use(Head, (self: Page) => ({ title: `${self.section} — Ramonda` }));
The callback form follows the value.
It works on both sides through one code path
Head writes to document.head. That is the same operation on the client and on the server —
renderToString runs under a DOM — so there is no second implementation to keep in step.
The alternative, a module-level map of collected tags, is how most head libraries do it and is exactly what this codebase refuses: module scope is shared by concurrent requests, so two renders in flight would read each other's title.
A <meta> must be identifiable
meta: [{ property: "og:type", content: "article" }] // ✓
meta: [{ content: "no key" }] // ✗ type error
Exactly one of name / property / httpEquiv identifies a tag, and that identity is what an
update replaces. Without it there is no way to find the tag again, so every update would append
another copy — and the failure is invisible until a page has been open long enough to accumulate
them.
On hydration
Applying is an upsert. The client finds the server's tags and updates them in place rather than doubling every one.