Skip to content

Goshtoso App Shells

Console Shell

An application layout with a persistent header and sidebar, a mobile drawer, and HTMX page navigation.

Try it live

A working example with its own navigation, theme controls, and viewport.

Open full example ↗

Switch routes without losing the application frame. Try the navigation menu at mobile size.

Setup and integration

Install and use

Install
go get github.com/araihu/goshtoso-app-shells/[email protected]
Asset handlers and layout
import (
    "net/http"

    "github.com/araihu/goshtoso/assets"
    "github.com/araihu/goshtoso/components/sidebar"
    "github.com/araihu/goshtoso-app-shells/consoleshell"
    shellassets "github.com/araihu/goshtoso-app-shells/consoleshell/assets"
)

func registerAssets(mux *http.ServeMux) {
    mux.Handle("GET /assets/", assets.Handler())
    mux.Handle("GET /consoleshell/assets/", shellassets.Handler())
}

cfg := consoleshell.Config{
    Brand: consoleshell.Brand{Name: "Operations", HomeURL: "/"},
    Navigation: consoleshell.Navigation{
        Items: []sidebar.Item{{
            ID: "runs", Label: "Runs", Href: "/runs",
        }},
    },
    Interactions: consoleshell.InteractionConfig{
        EnableHTMX: true,
        NavigationOOB: true,
        LocalRuntime: true,
    },
}
page := consoleshell.Page{
    Title: "Runs",
    Active: "runs",
    Content: runsPage(),
}
view := consoleshell.Layout(cfg, page)

Full documents and HTMX fragments

Return Layout for ordinary browser requests. With EnableHTMX enabled, return Fragment when HX-Request-Type is partial. This updates the current page without inserting a second HTML document.

Console Shell Fragment updates the document title and main content. Enable NavigationOOB to replace the sidebar in the same response and update its active link.

HTTP response selection
func renderConsole(
    writer http.ResponseWriter,
    request *http.Request,
    cfg consoleshell.Config,
    page consoleshell.Page,
) {
    view := consoleshell.Layout(cfg, page)
    if request.Header.Get("HX-Request-Type") == "partial" {
        view = consoleshell.Fragment(cfg, page)
    }
    if err := view.Render(request.Context(), writer); err != nil {
        http.Error(writer, err.Error(), http.StatusInternalServerError)
    }
}

What you provide

Configure the brand, navigation links, and page metadata, then pass in your content. Your application handles routes, permissions, and data.