Shared layouts
An optional src/layout.tsx wraps every page's HTML — header, footer, nav — without reopening component composition.
The data-nexa-slot element
// src/layout.tsx
export default function Layout() {
return (
<div>
<header>My site</header>
<div data-nexa-slot></div>
<footer>© 2026</footer>
</div>
);
}It must be empty, and there must be exactly one in the whole layout — that's where each page's already-rendered HTML gets inserted, verbatim.
Why this isn't composition
// Rust renders the layout, finds the ONE data-nexa-slot
// (must be empty), and inserts there the HTML the page
// already rendered — a text splice, not a function call.The layout compiles just like any page. Neither the layout nor the page know about each other before that final splice.
export const head: the one thing that really reaches <head>
// src/layout.tsx
export const head = {
icon: "/static/logo.svg",
appleTouchIcon: "/static/icon-180.png",
stylesheets: ["/static/site.css"]
};
export default function Layout() { /* ... */ }Everything else the layout returns ends up inside <body> (via the splice above) — a <link rel="icon"> placed there gets stuck in <body>, where not every browser detects it reliably (this very site had exactly that bug). export const head = { icon, appleTouchIcon, stylesheets } is resolved separately and merged into the document's real <head> — same pattern as seo/schema, a literal object, never executed code.