Shells

The three root layouts you can extend, when to pick each, and what is not a shell

Page actions AI-ready formats and sharing
Open LLM text
Share with AI
Ask Claude Ask ChatGPT Ask Gemini Ask Copilot

What a shell is

A shell is the root layout your page templates extend. It owns the document root (<html>, <head>, <body>), loads htmx, and declares the htmx-boost contract — the target id, swap mode, andhx-selectfilter that govern how boosted navigation flows into the page.

Pick exactly one shell per app. Your page templates, fragments, and feature modules render inside the shell's outlet.

The three shells

Chirp ships two; chirp-ui adds one more.

Shell Comes from When to pick it
chirp/layouts/boost.html core htmx-boost SPA-style nav, no opinionated chrome
chirp/layouts/shell.html core Fragment-only apps (LLM/RAG playgrounds, dashboards, form-heavy UIs) — nohx-select, fragments flow exactly where hx-targetsays
chirpui/app_shell_layout.html chirp-ui Sidebar/topbar app chrome with breadcrumbs, shell actions, and OOB regions pre-wired

The decisive question is whether your app needs persistent chrome — sidebar, topbar, breadcrumbs that survive boosted navigation:

  • Yesapp_shell_layout.html. See App Shells.
  • No, but you want SPA-style navboost.html. See Boosted Navigation.
  • No, fragment swaps onlyshell.html.

Thehx-selectdistinction

The biggest hidden difference between the three is what the outlet element (#main) sets for hx-select:

  • boost.htmlhx-select="#page-content". Filters every response. Correct for boosted nav; silently discards fragment responses that don't contain#page-content.
  • shell.html — no hx-select. Forms and fragment swaps land exactly wherehx-targetsays, with no filtering.
  • app_shell_layout.htmlhx-select="#page-content" on its <main> outlet. Same boost contract asboost.html, plus persistent chrome.

chirp check flags this mismatch via the select_inheritancerule (a WARNING) when a mutating element may silently discard its response. See the select_inheritance contract rule.

What is not a shell

Feature modules likechirp.docsship templates with their own visual chrome (sidebar nav, search box, content area). They look shell-like, but they are not shells — they render inside the outlet of whatever shell you extend. A feature module's page templates do not establish<html>/<head>/<body>, do not load htmx, and do not declare the boost contract. They render as the content of a route handler (typicallyPage("chirp_docs/doc_page.html", "doc_content", doc=doc)) and compose into the shell's {% block content %} slot.

Mountchirp.docs in an app that extends app_shell_layout.htmland the docs sidebar lives inside the chirp-ui main outlet — not as a peer of the chirp-ui sidebar. The shell stays in charge of the page frame.