Guide / 06

Layouts & Nesting

Layouts allow you to define shared HTML structures (navbar, sidebar, footer) that wrap child pages. They are automatically chained based on the directory hierarchy.

Creating a layout

The pages/@layout.stew file:

<goscript>
    import "os"
    import "github.com/ZiplEix/stew/sdk/live"
</goscript>

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8"/>
        <title>My App</title>
    </head>
    <body>
        <nav>...Navbar...</nav>

        <slot />    <!-- The page content is inserted here -->

        {{ if os.Getenv("STEW_DEV") == "true" }}
            {{ raw(live.InjectScript()) }}
        {{ end }}
    </body>
</html>
<slot />: The <slot /> tag is the injection point for child content. Without it, the page would not be displayed within the layout.

Nested Layouts

Each subfolder can have its own @layout.stew. Layouts chain from the root to the leaf:

Rendering of GET /guide/installation:

pages/@layout.stew       β†’ HTML shell (DOCTYPE, head, body)
  └── pages/guide/@layout.stew β†’ Navigation sidebar
        └── pages/guide/installation/@page.stew β†’ Content

The generated router automatically creates the closure chain:

pages.Layout(w, data, func() {
    stew_pages_guide.Layout(w, data, func() {
        stew_pages_guide_installation.Page(w, data)
    })
})

Go Signature of a Layout

The compiler generates for @layout.stew:

func Layout(w io.Writer, data stew.PageData, slot func()) {
    // HTML rendering before the slot
    w.Write([]byte(`...`))

    // Slot injection
    if slot != nil {
        slot()
    }

    // HTML rendering after the slot
    w.Write([]byte(`...`))
}

PageData in Layouts

The layout receives the same data stew.PageData object as the page. It can therefore access the current URL to highlight the active link in a navigation:

<goscript>
    getLinkClass := func(target string) string {
        if data.URL == target {
            return "font-bold text-amber-600"
        }
        return "text-stone-600"
    }
</goscript>

<nav>
    <a href="/" class="{{ getLinkClass("/") }}">Home</a>
    <a href="/blog" class="{{ getLinkClass("/blog") }}">Blog</a>
</nav>