Suspense Dashboard

Shell-first initial render with deferred blocks and OOB swaps

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

What you get

This example builds a sales dashboard whose shell paints instantly while three slow data sources — revenue, visitors, and recent orders — fill in afterward. It all happens in a single HTTP response, with no extra client requests and no JavaScript framework.

Copy this example when a page has several independent slow queries and you want an instant first paint instead of one spinner blocking the whole page. The return type that does the work is [[docs/about/core-concepts/return-values|Suspense]].

The handler

The whole feature is one return type. Each awaitable in the context is deferred: the shell renders first, then each value streams back as it resolves.

from chirp import App, AppConfig, Suspense

config = AppConfig(template_dir=TEMPLATES_DIR, worker_mode="async")
app = App(config=config)


@app.route("/")
def dashboard():
    """Shell renders instantly, data blocks fill in as they resolve."""
    return Suspense(
        "dashboard.html",
        title="Sales Dashboard",     # sync — in the shell
        revenue=load_revenue(),      # awaitable — deferred
        orders=load_orders(),        # awaitable — deferred
        visitors=load_visitors(),    # awaitable — deferred
    )

Source: examples/standalone/suspense_dashboard/app.py.

In the template, branch onis deferredto show a skeleton until the value arrives, then render the loaded state:

{% block revenue %}
  {% if revenue is deferred %}
    <div class="skeleton">Loading revenue…</div>
  {% else %}
    <p>{{ revenue.total }} ({{ revenue.period }})</p>
  {% endif %}
{% endblock %}

Each resolved block streams back as an OOB swap that replaces its skeleton in place.

Run it

PYTHONPATH=src python examples/standalone/suspense_dashboard/app.py

Open http://127.0.0.1:8000/.

Test it

pytest examples/standalone/suspense_dashboard/