The Index Pattern

Separation of concerns without the complexity. Use index.py for server-side logic and index.html for reactive markup.

index.py

The Controller. Handles database queries, metadata definitions, and heavy lifting. It executes strictly on the server.

  • Defines Page Metadata (Title, Desc)
  • Fetches Data (Prisma ORM)
  • Validates Permissions

index.html

The View. Defines the layout and interactivity using PulsePoint. It receives data implicitly from the Python controller.

  • Reactive UI (pp.state, pp.effect)
  • Tailwind CSS Styling
  • Zero-Build Component Imports

File-System Routing

Routes are defined automatically by your folder structure. A folder becomes a public URL route if it contains either an index.html or an index.py.

! Routing Hierarchy: Python Takes Precedence

If a folder contains both files, the server designates index.py as the primary entry point. The HTML file is ignored by the router and will only be rendered if the Python script explicitly loads it (e.g., via load_page(__file__)).

Project Structure
app/index.py
/
└── app/dashboard/index.py
/dashboard
└── app/dashboard/settings/index.py
/dashboard/settings

1. The Logic Layer

src/app/index.py

The Python file must export a page() function. This is where you define page-specific metadata and load the view.

from casp.page import load_page

# 1. Define Metadata automatically injected into <head>
title = "Caspian | The Native Python Web Framework"
description = "Build reactive, high-performance web applications."

# 2. The entry point function
def page():
    # Logic goes here (DB calls, Auth checks)
    return load_page(__file__)

2. The Reactive View

src/app/index.html

The HTML file contains your UI layout. You can import PulsePoint icons, use Tailwind classes, and write reactive JavaScript directly in <script> tags.

<!-- Import Icons or Components -->
<!-- @import { Sparkles } from ../../lib/ppicons -->

<div class="p-10 text-center space-y-4">
  <h1 class="text-4xl font-bold">
    Welcome to Caspian
  </h1>
  
  <!-- Reactive Elements -->
  <button 
    class="btn btn-primary"
    onclick="handleClick()"
  >
    <Sparkles class="mr-2 size-4" />
    Click me: {count}
  </button>
</div>

<script>
  // Native Reactivity
  const [count, setCount] = pp.state(0);

  function handleClick() {
    setCount(count + 1);
  }
</script>