Menu
Public repository / 439f0dcabf5cdf2b120c7724be895cf1df30507f
AkurAI-Framework / site/content/docs/layouts.md
Latest change 7d64b4511fc8d94d569ca5b7df933a213ab69a05 - feat(cli): route layouts (shared shell templates) by Ólafur Búi Ólafsson
# Route layouts
A **layout** is a shared shell template that wraps a page. It is the
AkurAI-Framework take on the nested-layout idea from SvelteKit and Next.js:
write the surrounding chrome once, then point any number of routes at it.
Layouts are opt-in and fully backward compatible — a route without a layout
renders exactly as it did before.
## Declaring a layout
Add an optional `"layout"` field to a route in `backend/routes.json`. Its value
is the file stem of a template in `frontend/` (a `*.html` file), just like
`template`:
```json
{
"routes": [
{ "path": "/", "template": "index" },
{
"path": "/welcome",
"template": "welcome",
"layout": "marketing"
}
]
}
```
Existing fields keep working untouched. You can combine `layout` with the SEO
`title`/`description` fields on the same route.
## How composition works
When a matched route declares a layout, the server renders in two passes:
1. The page `template` is rendered with the usual context (route `params`, any
per-route `meta_*`, and the project-wide `page.json` context).
2. That rendered HTML is injected back into the **same context** under the key
`content`, and the `layout` template is rendered. The layout pulls the page
output in with the raw-output tag `{{{ content }}}`.
This is the same one-pass-into-a-shell composition the docs pages already use —
now available declaratively to any route.
A route with **no** `layout` renders in a single pass, exactly as before.
## Writing a layout template
A layout owns the document shell (head, header, footer) and drops the page where
`{{{ content }}}` appears:
```html
{% include "header" %}
<main class="container">
<div class="marketing-shell">
{{{ content }}}
</div>
</main>
{% include "footer" %}
```
The matching page template then holds **only its own content** — no
`<head>`, header, or footer, since the layout supplies those:
```html
<section class="page-head">
<h1>Welcome to the marketing shell</h1>
<p class="lede">This fragment is wrapped by the shared layout.</p>
</section>
```
The page fragment and the layout both see the full render context, so either can
read `{{ params.* }}`, `{{ title }}`, `nav`, and anything else from `page.json`.
## Live demo
The site itself ships a working example: the [`/welcome`](/welcome) route uses
the `marketing` layout to wrap the content-only `welcome` page template.