Quickstart

Build your first Chirp application in 5 minutes

2 min read 454 words

Scaffold a Project

The fastest way to start is thechirp newcommand:

chirp new myapp
cd myapp
python app.py

Open http://127.0.0.1:8000 in your browser. This generates an app.py, a templates/ directory with base.html and index.html, a static/ directory with style.css, and a tests/directory with a smoke test.

For an even smaller starting point:

chirp new myapp --minimal

This generates only app.py and templates/index.html.

Hello World (Manual)

You can also create a project by hand. Create a file calledapp.py:

from chirp import App

app = App()

@app.route("/")
def index():
    return "Hello, World!"

app.run()

Run it:

python app.py

Open http://127.0.0.1:8000in your browser. Five lines to hello world.

Add Templates

Create atemplates/ directory and add base.html:

<!DOCTYPE html>
<html>
<head><title>{{ title }}</title></head>
<body>
  {% block content %}{% endblock %}
</body>
</html>

Add templates/index.html:

{% extends "base.html" %}

{% block content %}
  <h1>{{ title }}</h1>
  <p>Welcome to my Chirp app.</p>
{% endblock %}

Update app.py:

from chirp import App, Template

app = App()

@app.route("/")
def index():
    return Template("index.html", title="Home")

app.run()

Route functions return values. Template tells the framework to render index.htmlwith the given context via kida.

Fragment Rendering

This is where Chirp diverges from Flask. Add a search feature that works as both a full page and an htmx fragment.

Addtemplates/search.html:

{% extends "base.html" %}

{% block content %}
  <h1>Search</h1>
  <input type="search" name="q"
         hx-get="/search" hx-target="#results" hx-trigger="input changed delay:300ms">

  {% block results %}
    <div id="results">
      {% for item in results %}
        <p>{{ item }}</p>
      {% endfor %}
    </div>
  {% endblock %}
{% endblock %}

Update app.py:

from chirp import App, Template, Fragment, Request

app = App()

ITEMS = ["apple", "banana", "cherry", "date", "elderberry"]

@app.route("/")
def index():
    return Template("index.html", title="Home")

@app.route("/search")
def search(request: Request):
    q = request.query.get("q", "")
    results = [i for i in ITEMS if q.lower() in i.lower()] if q else ITEMS

    if request.is_fragment:
        return Fragment("search.html", "results", results=results)
    return Template("search.html", title="Search", results=results)

app.run()

Full page navigation renders everything. An htmx request renders just the resultsblock. Same template, same data, different scope.

Add htmx

To make the fragment rendering work, include htmx in yourbase.html:

<!DOCTYPE html>
<html>
<head>
  <title>{{ title }}</title>
  <script src="https://unpkg.com/htmx.org@2.0.4"></script>
</head>
<body>
  {% block content %}{% endblock %}
</body>
</html>

Now the search input sends requests to /search via htmx, and Chirp responds with just the resultsblock -- no full page reload, no separate partials directory, no JavaScript.

Next Steps