The Layout Pattern

Define your application's shell once. Use layout.html for shared UI and layout.py to control the context dynamically.

Anatomy of a View

layout.html (Shell)
{{ children }}
index.html (Page)

Core Principles

  • 1

    Strict Precedence

    If layout.py exists, it is the sole authority. It must return the HTML content explicitly.

  • 2

    Props Bubbling

    Data flows upwards. Properties defined in a child layout (e.g., /docs/layout.py) override properties in the root layout.

  • 3

    Nested Wrapping

    Layouts render from the inside out. The page renders inside the leaf layout, which renders inside the parent layout, up to the root.

The Controller

Python
from casp.layout import load_layout

# 1. Define the logic
def layout(context: dict):
    
    props = {
        "title": "My App",
        "body_class": "antialiased bg-background",
        "user": context.get("user")
    }

    # 2. Return Tuple: (HTML Content, Props)
    # Strict Mode: You MUST load the file explicitly
    return load_layout(__file__), props
Return Types
tuple(str, dict) Returns HTML template + Dynamic Properties. Most common.
str Returns raw HTML only. No props.
dict Returns props only. Warning: HTML defaults to pass-through.

The View

HTML
<!-- Access props via layout.* -->
<html>
  <head>
    <title>{{ layout.title }}</title>
  </head>
  <body class="{{ layout.body_class }}">
    
    <nav>...</nav>

    <!-- The content injection point -->
    {{ children | safe }}

  </body>
</html>

Route Groups & Multiple Root Layouts

Sometimes you need completely different layouts for different parts of your app (e.g., a Marketing landing page vs. an App Dashboard). You can use Route Groups by wrapping folder names in parenthesis (name).

Project Structure
src/app
(marketing)
layout.html Marketing UI
index.html
(dashboard)
layout.html Admin UI
index.html

How it works

Folders in parenthesis are omitted from the URL.

  • app/(marketing)/index.html/
  • app/(dashboard)/index.html/dashboard *

* Assuming dashboard is defined inside the group or handled via index precedence.

Benefit

This allows you to define a Marketing Layout (Navbar, Footer, Hero) that is completely different from your Dashboard Layout (Sidebar, Header, Graphs) without conditional logic spaghetti.