Contacts Shell

Contacts CRUD rebuilt with chirp-ui app shell and mounted pages

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

What It Teaches

This is the same contacts CRUD app as Contacts, rebuilt on the chirp-ui app shell — a persistent page chrome (sidebar, topbar) that stays put while htmx swaps the content region. Reach for this version once you have outgrown isolated fragments and want filesystem-mounted pages, URL-backed search state, and shell-aware navigation.

It demonstrates:

  • use_chirp_ui(app) and app.mount_pages()
  • chirpui/app_shell_layout.htmlfor the persistent shell
  • route-scoped shell actions from_context.py
  • query-backed search state
  • inline row editing without stale filtered results
  • typed repeated-field parsing with [[docs/build-apps/forms-data/forms-validation|form_from()]]

When to Reach for It

Both contacts examples cover the same CRUD domain. Pick by the UI layer you need.

Example UI layer Use it for
Contacts Plain htmx, no shell The fragment / OOB / validation baseline in a single template
Contacts Shell chirp-ui app shell + mounted pages Persistent chrome, filesystem pages, URL-backed search state

Minimal Example

The whole app is about 50 lines:use_chirp_uiinstalls the shell, mount_pageswires the filesystem routes, and one explicit route parses a repeatedcontact_ids field with form_from.

import sys
from dataclasses import dataclass
from pathlib import Path

from chirp import App, AppConfig, Fragment, Request, form_from, use_chirp_ui
from chirp.middleware.csrf import CSRFMiddleware
from chirp.middleware.sessions import SessionConfig, SessionMiddleware

ROOT_DIR = Path(__file__).parent
PAGES_DIR = ROOT_DIR / "pages"

sys.path.insert(0, str(ROOT_DIR))

from chirp_ui import register_colors
from contacts_shell_store import GROUP_COLORS, reset_store, store


@dataclass(frozen=True, slots=True)
class ContactSelectionForm:
    contact_ids: list[int]


config = AppConfig(template_dir=PAGES_DIR, debug=True)
app = App(config=config)

use_chirp_ui(app)
register_colors(GROUP_COLORS)
app.add_middleware(SessionMiddleware(SessionConfig(secret_key="contacts-shell-dev-secret")))
app.add_middleware(CSRFMiddleware())
reset_store()
app.mount_pages(str(PAGES_DIR))


@app.route("/contacts/selection", methods=["POST"])
async def select_contacts(request: Request):
    form = await form_from(request, ContactSelectionForm)
    selected = [contact for contact_id in form.contact_ids if (contact := store.get(contact_id))]
    return Fragment("contacts/selection.html", "selection_preview", selected_contacts=selected)


if __name__ == "__main__":
    app.run()

Source: examples/chirpui/contacts_shell/app.py.

Thecontact_ids: list[int]annotation is the point of the selection route: form_from parses repeated contact_idsform fields into a typed list of ints.

Run It

PYTHONPATH=src python examples/chirpui/contacts_shell/app.py

Open http://127.0.0.1:8000/.

Test It

pytest examples/chirpui/contacts_shell/

Source

Next