<project title="FastHTML" summary='FastHTML is a python library which brings together Starlette, Uvicorn, HTMX, and fastcore&#39;s `FT` "FastTags" into a library for creating server-rendered hypermedia applications. The `FastHTML` class itself inherits from `Starlette`, and adds decorator-based routing with many additions, Beforeware, automatic `FT` to HTML rendering, and much more.'>Things to remember when writing FastHTML apps:

- Although parts of its API are inspired by FastAPI, it is *not* compatible with FastAPI syntax and is not targeted at creating API services
- FastHTML includes support for Pico CSS and the fastlite sqlite library, although using both are optional; sqlalchemy can be used directly or via the fastsql library, and any CSS framework can be used. Support for the Surreal and css-scope-inline libraries are also included, but both are optional
- FastHTML is compatible with JS-native web components and any vanilla JS library, but not with React, Vue, or Svelte
- Use `serve()` for running uvicorn (`if __name__ == "__main__"` is not needed since it's automatic)
- When a title is needed with a response, use `Titled`; note that that already wraps children in `Container`, and already includes both the meta title as well as the H1 element.<docs><doc title="FastHTML concise guide" desc="A brief overview of idiomatic FastHTML apps"># Concise reference



## About FastHTML

``` python
from fasthtml.common import *
```

FastHTML is a python library which brings together Starlette, Uvicorn,
HTMX, and fastcore’s `FT` “FastTags” into a library for creating
server-rendered hypermedia applications. The
[`FastHTML`](https://www.fastht.ml/docs/api/core.html#fasthtml) class
itself inherits from `Starlette`, and adds decorator-based routing with
many additions, Beforeware, automatic `FT` to HTML rendering, and much
more.

Things to remember when writing FastHTML apps:

- *Not* compatible with FastAPI syntax; FastHTML is for HTML-first apps,
  not API services (although it can implement APIs too)
- FastHTML includes support for Pico CSS and the fastlite sqlite
  library, although using both are optional; sqlalchemy can be used
  directly or via the fastsql library, and any CSS framework can be
  used. MonsterUI is a richer FastHTML-first component framework with
  similar capabilities to shadcn
- FastHTML is compatible with JS-native web components and any vanilla
  JS library, but not with React, Vue, or Svelte
- Use [`serve()`](https://www.fastht.ml/docs/api/core.html#serve) for
  running uvicorn (`if __name__ == "__main__"` is not needed since it’s
  automatic)
- When a title is needed with a response, use
  [`Titled`](https://www.fastht.ml/docs/api/xtend.html#titled); note
  that that already wraps children in
  [`Container`](https://www.fastht.ml/docs/api/pico.html#container), and
  already includes both the meta title as well as the H1 element.

## Minimal App

The code examples here use fast.ai style: prefer ternary op, 1-line
docstring, minimize vertical space, etc. (Normally fast.ai style uses
few if any comments, but they’re added here as documentation.)

A minimal FastHTML app looks something like this:

``` python
# Meta-package with all key symbols from FastHTML and Starlette. Import it like this at the start of every FastHTML app.
from fasthtml.common import *
# The FastHTML app object and shortcut to `app.route`
app,rt = fast_app()

# Enums constrain the values accepted for a route parameter
name = str_enum('names', 'Alice', 'Bev', 'Charlie')

# Passing a path to `rt` is optional. If not passed (recommended), the function name is the route ('/foo')
# Both GET and POST HTTP methods are handled by default
# Type-annotated params are passed as query params (recommended) unless a path param is defined (which it isn't here)
@rt
def foo(nm: name):
    # `Title` and `P` here are FastTags: direct m-expression mappings of HTML tags to Python functions with positional and named parameters. All standard HTML tags are included in the common wildcard import.
    # When a tuple is returned, this returns concatenated HTML partials. HTMX by default will use a title HTML partial to set the current page name. HEAD tags (e.g. Meta, Link, etc) in the returned tuple are automatically placed in HEAD; everything else is placed in BODY.
    # FastHTML will automatically return a complete HTML document with appropriate headers if a normal HTTP request is received. For an HTMX request, however, just the partials are returned.
    return Title("FastHTML"), H1("My web app"), P(f"Hello, {name}!")
# By default `serve` runs uvicorn on port 5001. Never write `if __name__ == "__main__"` since `serve` checks it internally.
serve()
```

To run this web app:

``` bash
python main.py  # access via localhost:5001
```

## FastTags (aka FT Components or FTs)

FTs are m-expressions plus simple sugar. Positional params map to
children. Named parameters map to attributes. Aliases must be used for
Python reserved words.

``` python
tags = Title("FastHTML"), H1("My web app"), P(f"Let's do this!", cls="myclass")
tags
```

    (title(('FastHTML',),{}),
     h1(('My web app',),{}),
     p(("Let's do this!",),{'class': 'myclass'}))

This example shows key aspects of how FTs handle attributes:

``` python
Label(
    "Choose an option", 
    Select(
        Option("one", value="1", selected=True),  # True renders just the attribute name
        Option("two", value=2, selected=False),   # Non-string values are converted to strings. False omits the attribute entirely
        cls="selector", id="counter",             # 'cls' becomes 'class'
        **{'@click':"alert('Clicked');"},         # Dict unpacking for attributes with special chars
    ),
    _for="counter",                               # '_for' becomes 'for' (can also use 'fr')
)
```

Classes with `__ft__` defined are rendered using that method.

``` python
class FtTest:
    def __ft__(self): return P('test')
    
to_xml(FtTest())
```

    '<p>test</p>\n'

You can create new FTs by importing the new component from
`fasthtml.components`. If the FT doesn’t exist within that module,
FastHTML will create it.

``` python
from fasthtml.components import Some_never_before_used_tag

Some_never_before_used_tag()
```

``` html
<some-never-before-used-tag></some-never-before-used-tag>
```

FTs can be combined by defining them as a function.

``` python
def Hero(title, statement): return Div(H1(title),P(statement), cls="hero")
to_xml(Hero("Hello World", "This is a hero statement"))
```

    '<div class="hero">\n  <h1>Hello World</h1>\n  <p>This is a hero statement</p>\n</div>\n'

When handling a response, FastHTML will automatically render FTs using
the `to_xml` function.

``` python
to_xml(tags)
```

    '<title>FastHTML</title>\n<h1>My web app</h1>\n<p class="myclass">Let&#x27;s do this!</p>\n'

## JS

The [`Script`](https://www.fastht.ml/docs/api/xtend.html#script)
function allows you to include JavaScript. You can use Python to
generate parts of your JS or JSON like this:

``` python
# In future snippets this import will not be shown, but is required
from fasthtml.common import * 
app,rt = fast_app(hdrs=[Script(src="https://cdn.plot.ly/plotly-2.32.0.min.js")])
# `index` is a special function name which maps to the `/` route. 
@rt
def index():
    data = {'somedata':'fill me in…'}
    # `Titled` returns a title tag and an h1 tag with the 1st param, with remaining params as children in a `Main` parent.
    return Titled("Chart Demo", Div(id="myDiv"), Script(f"var data = {data}; Plotly.newPlot('myDiv', data);"))
# In future snippets `serve() will not be shown, but is required
serve()
```

Prefer Python whenever possible over JS. Never use React or shadcn.

## fast_app hdrs

``` python
# In future snippets we'll skip showing the `fast_app` call if it has no params
app, rt = fast_app(
    pico=False, # The Pico CSS framework is included by default, so pass `False` to disable it if needed. No other CSS frameworks are included.
    # These are added to the `head` part of the page for non-HTMX requests.
    hdrs=(
        Link(rel='stylesheet', href='assets/normalize.min.css', type='text/css'),
        Link(rel='stylesheet', href='assets/sakura.css', type='text/css'),
        Style("p {color: red;}"),
        # `MarkdownJS` and `HighlightJS` are available via concise functions
        MarkdownJS(), HighlightJS(langs=['python', 'javascript', 'html', 'css']),
        # by default, all standard static extensions are served statically from the web app dir,
        #   which can be modified using e.g `static_path='public'`
        )
)

@rt
def index(req): return Titled("Markdown rendering example",
                              # This will be client-side rendered to HTML with highlight-js
                              Div("*hi* there",cls="marked"),
                              # This will be syntax highlighted
                              Pre(Code("def foo(): pass")))
```

## Responses

Routes can return various types:

1.  FastTags or tuples of FastTags (automatically rendered to HTML)
2.  Standard Starlette responses (used directly)
3.  JSON-serializable types (returned as JSON in a plain text response)

``` python
@rt("/{fname:path}.{ext:static}")
async def serve_static_file(fname:str, ext:str): return FileResponse(f'public/{fname}.{ext}')

app, rt = fast_app(hdrs=(MarkdownJS(), HighlightJS(langs=['python', 'javascript'])))
@rt
def index(): 
    return Titled("Example",
                  Div("*markdown* here", cls="marked"),
                  Pre(Code("def foo(): pass")))
```

Route functions can be used in attributes like `href` or `action` and
will be converted to paths. Use `.to()` to generate paths with query
parameters.

``` python
@rt
def profile(email:str): return fill_form(profile_form, profiles[email])

profile_form = Form(action=profile)(
    Label("Email", Input(name="email")),
    Button("Save", type="submit")
)

user_profile_path = profile.to(email="user@example.com")  # '/profile?email=user%40example.com'
```

``` python
from dataclasses import dataclass

app,rt = fast_app()
```

When a route handler function is used as a fasttag attribute (such as
`href`, `hx_get`, or `action`) it is converted to that route’s path.
[`fill_form`](https://www.fastht.ml/docs/api/components.html#fill_form)
is used to copy an object’s matching attrs into matching-name form
fields.

``` python
@dataclass
class Profile: email:str; phone:str; age:int
email = 'john@example.com'
profiles = {email: Profile(email=email, phone='123456789', age=5)}
@rt
def profile(email:str): return fill_form(profile_form, profiles[email])

profile_form = Form(method="post", action=profile)(
        Fieldset(
            Label('Email', Input(name="email")),
            Label("Phone", Input(name="phone")),
            Label("Age", Input(name="age"))),
        Button("Save", type="submit"))
```

## Testing

We can use `TestClient` for testing.

``` python
from starlette.testclient import TestClient
```

``` python
path = "/profile?email=john@example.com"
client = TestClient(app)
htmx_req = {'HX-Request':'1'}
print(client.get(path, headers=htmx_req).text)
```

    <form enctype="multipart/form-data" method="post" action="/profile"><fieldset><label>Email       <input name="email" value="john@example.com">
    </label><label>Phone       <input name="phone" value="123456789">
    </label><label>Age       <input name="age" value="5">
    </label></fieldset><button type="submit">Save</button></form>

## Form Handling and Data Binding

When a dataclass, namedtuple, etc. is used as a type annotation, the
form body will be unpacked into matching attribute names automatically.

``` python
@rt
def edit_profile(profile: Profile):
    profiles[email]=profile
    return RedirectResponse(url=path)

new_data = dict(email='john@example.com', phone='7654321', age=25)
print(client.post("/edit_profile", data=new_data, headers=htmx_req).text)
```

    <form enctype="multipart/form-data" method="post" action="/profile"><fieldset><label>Email       <input name="email" value="john@example.com">
    </label><label>Phone       <input name="phone" value="7654321">
    </label><label>Age       <input name="age" value="25">
    </label></fieldset><button type="submit">Save</button></form>

## fasttag Rendering Rules

The general rules for rendering children inside tuples or fasttag
children are: - `__ft__` method will be called (for default components
like `P`, `H2`, etc. or if you define your own components) - If you pass
a string, it will be escaped - On other python objects, `str()` will be
called

If you want to include plain HTML tags directly into e.g. a `Div()` they
will get escaped by default (as a security measure to avoid code
injections). This can be avoided by using `Safe(...)`, e.g to show a
data frame use `Div(NotStr(df.to_html()))`.

## Exceptions

FastHTML allows customization of exception handlers.

``` python
def not_found(req, exc): return Titled("404: I don't exist!")
exception_handlers = {404: not_found}
app, rt = fast_app(exception_handlers=exception_handlers)
```

## Cookies

We can set cookies using the
[`cookie()`](https://www.fastht.ml/docs/api/core.html#cookie) function.

``` python
@rt
def setcook(): return P(f'Set'), cookie('mycookie', 'foobar')
print(client.get('/setcook', headers=htmx_req).text)
```

     <p>Set</p>

``` python
@rt
def getcook(mycookie:str): return f'Got {mycookie}'
# If handlers return text instead of FTs, then a plaintext response is automatically created
print(client.get('/getcook').text)
```

    Got foobar

FastHTML provide access to Starlette’s request object automatically
using special `request` parameter name (or any prefix of that name).

``` python
@rt
def headers(req): return req.headers['host']
```

## Request and Session Objects

FastHTML provides access to Starlette’s session middleware automatically
using the special `session` parameter name (or any prefix of that name).

``` python
@rt
def profile(req, sess, user_id: int=None):
    ip = req.client.host
    sess['last_visit'] = datetime.now().isoformat()
    visits = sess.setdefault('visit_count', 0) + 1
    sess['visit_count'] = visits
    user = get_user(user_id or sess.get('user_id'))
    return Titled(f"Profile: {user.name}", 
                  P(f"Visits: {visits}"), 
                  P(f"IP: {ip}"),
                  Button("Logout", hx_post=logout))
```

Handler functions can return the
[`HtmxResponseHeaders`](https://www.fastht.ml/docs/api/core.html#htmxresponseheaders)
object to set HTMX-specific response headers.

``` python
@rt
def htmlredirect(app): return HtmxResponseHeaders(location="http://example.org")
```

## APIRouter

[`APIRouter`](https://www.fastht.ml/docs/api/core.html#apirouter) lets
you organize routes across multiple files in a FastHTML app.

``` python
# products.py
ar = APIRouter()

@ar
def details(pid: int): return f"Here are the product details for ID: {pid}"

@ar
def all_products(req):
    return Div(
        Div(
            Button("Details",hx_get=details.to(pid=42),hx_target="#products_list",hx_swap="outerHTML",),
        ), id="products_list")
```

``` python
# main.py
from products import ar,all_products

app, rt = fast_app()
ar.to_app(app)

@rt
def index():
    return Div(
        "Products",
        hx_get=all_products, hx_swap="outerHTML")
```

## Toasts

Toasts can be of four types:

- info
- success
- warning
- error

Toasts require the use of the `setup_toasts()` function, plus every
handler needs:

- The session argument
- Must return FT components

``` python
setup_toasts(app)

@rt
def toasting(session):
    add_toast(session, f"cooked", "info")
    add_toast(session, f"ready", "success")
    return Titled("toaster")
```

`setup_toasts(duration)` allows you to specify how long a toast will be
visible before disappearing.10 seconds.

Authentication and authorization are handled with Beforeware, which
functions that run before the route handler is called.

## Auth

``` python
def user_auth_before(req, sess):
    # `auth` key in the request scope is automatically provided to any handler which requests it and can not be injected
    auth = req.scope['auth'] = sess.get('auth', None)
    if not auth: return RedirectResponse('/login', status_code=303)

beforeware = Beforeware(
    user_auth_before,
    skip=[r'/favicon\.ico', r'/static/.*', r'.*\.css', r'.*\.js', '/login', '/']
)

app, rt = fast_app(before=beforeware)
```

## Server-Side Events (SSE)

FastHTML supports the HTMX SSE extension.

``` python
import random
hdrs=(Script(src="https://unpkg.com/htmx-ext-sse@2.2.3/sse.js"),)
app,rt = fast_app(hdrs=hdrs)

@rt
def index(): return Div(hx_ext="sse", sse_connect="/numstream", hx_swap="beforeend show:bottom", sse_swap="message")

# `signal_shutdown()` gets an event that is set on shutdown
shutdown_event = signal_shutdown()

async def number_generator():
    while not shutdown_event.is_set():
        data = Article(random.randint(1, 100))
        yield sse_message(data)

@rt
async def numstream(): return EventStream(number_generator())
```

## Websockets

FastHTML provides useful tools for HTMX’s websockets extension.

``` python
# These HTMX extensions are available through `exts`:
#   head-support preload class-tools loading-states multi-swap path-deps remove-me ws chunked-transfer
app, rt = fast_app(exts='ws')

def mk_inp(): return Input(id='msg', autofocus=True)

@rt
async def index(request):
    # `ws_send` tells HTMX to send a message to the nearest websocket based on the trigger for the form element
    cts = Div(
        Div(id='notifications'),
        Form(mk_inp(), id='form', ws_send=True),
        hx_ext='ws', ws_connect='/ws')
    return Titled('Websocket Test', cts)

async def on_connect(send): await send(Div('Hello, you have connected', id="notifications"))
async def on_disconnect(ws): print('Disconnected!')

@app.ws('/ws', conn=on_connect, disconn=on_disconnect)
async def ws(msg:str, send):
    # websocket hander returns/sends are treated as OOB swaps
    await send(Div('Hello ' + msg, id="notifications"))
    return Div('Goodbye ' + msg, id="notifications"), mk_inp()
```

Sample chatbot that uses FastHTML’s `setup_ws` function:

``` py
app = FastHTML(exts='ws')
rt = app.route
msgs = []

@rt('/')
def home():
    return Div(hx_ext='ws', ws_connect='/ws')(
        Div(Ul(*[Li(m) for m in msgs], id='msg-list')),
        Form(Input(id='msg'), id='form', ws_send=True)
    )

async def ws(msg:str):
    msgs.append(msg)
    await send(Ul(*[Li(m) for m in msgs], id='msg-list'))

send = setup_ws(app, ws)
```

### Single File Uploads

[`Form`](https://www.fastht.ml/docs/api/xtend.html#form) defaults to
“multipart/form-data”. A Starlette UploadFile is passed to the handler.

``` python
upload_dir = Path("filez")

@rt
def index():
    return (
        Form(hx_post=upload, hx_target="#result")(
            Input(type="file", name="file"),
            Button("Upload", type="submit")),
        Div(id="result")
    )

# Use `async` handlers where IO is used to avoid blocking other clients
@rt
async def upload(file: UploadFile):
    filebuffer = await file.read()
    (upload_dir / file.filename).write_bytes(filebuffer)
    return P('Size: ', file.size)
```

For multi-file, use `Input(..., multiple=True)`, and a type annotation
of `list[UploadFile]` in the handler.

## Fastlite

Fastlite and the MiniDataAPI specification it’s built on are a
CRUD-oriented API for working with SQLite. APSW and apswutils is used to
connect to SQLite, optimized for speed and clean error handling.

``` python
from fastlite import *
```

``` python
db = database(':memory:') # or database('data/app.db')
```

Tables are normally constructed with classes, field types are specified
as type hints.

``` python
class Book: isbn: str; title: str; pages: int; userid: int
# The transform arg instructs fastlite to change the db schema when fields change.
# Create only creates a table if the table doesn't exist.
books = db.create(Book, pk='isbn', transform=True)
                
class User: id: int; name: str; active: bool = True
# If no pk is provided, id is used as the primary key.
users = db.create(User, transform=True)
users
```

    <Table user (id, name, active)>

### Fastlite CRUD operations

Every operation in fastlite returns a full superset of dataclass
functionality.

``` python
user = users.insert(name='Alex',active=False)
user
```

    User(id=1, name='Alex', active=0)

``` python
# List all records
users()
```

    [User(id=1, name='Alex', active=0)]

``` python
# Limit, offset, and order results:
users(order_by='name', limit=2, offset=1)

# Filter on the results
users(where="name='Alex'")

# Placeholder for avoiding injection attacks
users("name=?", ('Alex',))

# A single record by pk
users[user.id]
```

    User(id=1, name='Alex', active=0)

Test if a record exists by using `in` keyword on primary key:

``` python
1 in users
```

    True

Updates (which take a dict or a typed object) return the updated record.

``` python
user.name='Lauren'
user.active=True
users.update(user)
```

    User(id=1, name='Lauren', active=1)

`.xtra()` to automatically constrain queries, updates, and inserts from
there on:

``` python
users.xtra(active=True)
users()
```

    [User(id=1, name='Lauren', active=1)]

Deleting by pk:

``` python
users.delete(user.id)
```

    <Table user (id, name, active)>

NotFoundError is raised by pk `[]`, updates, and deletes.

``` python
try: users['Amy']
except NotFoundError: print('User not found')
```

    User not found

## MonsterUI

MonsterUI is a shadcn-like component library for FastHTML. It adds the
Tailwind-based libraries FrankenUI and DaisyUI to FastHTML, as well as
Python’s mistletoe for Markdown, HighlightJS for code highlighting, and
Katex for latex support, following semantic HTML patterns when possible.
It is recommended for when you wish to go beyond the basics provided by
FastHTML’s built-in pico support.

A minimal app:

``` python
from fasthtml.common import *
from monsterui.all import *

app, rt = fast_app(hdrs=Theme.blue.headers(highlightjs=True)) # Use MonsterUI blue theme and highlight code in markdown

@rt
def index():
    socials = (('github','https://github.com/AnswerDotAI/MonsterUI'),)
    return Titled("App",
        Card(
            P("App", cls=TextPresets.muted_sm),
            # LabelInput, DivLAigned, and UkIconLink are non-semantic MonsterUI FT Components,
            LabelInput('Email', type='email', required=True),
            footer=DivLAligned(*[UkIconLink(icon,href=url) for icon,url in socials])))
```

MonsterUI recommendations:

- Use defaults as much as possible, for example
  [`Container`](https://www.fastht.ml/docs/api/pico.html#container) in
  monsterui already has defaults for margins
- Use `*T` for button styling consistency, for example
  `cls=ButtonT.destructive` for a red delete button or
  `cls=ButtonT.primary` for a CTA button
- Use `Label*` functions for forms as much as possible
  (e.g. `LabelInput`, `LabelRange`) which creates and links both the
  `FormLabel` and user input appropriately to avoid boiler plate.

Flex Layout Elements (such as `DivLAligned` and `DivFullySpaced`) can be
used to create layouts concisely

``` python
def TeamCard(name, role, location="Remote"):
    icons = ("mail", "linkedin", "github")
    return Card(
        DivLAligned(
            DiceBearAvatar(name, h=24, w=24),
            Div(H3(name), P(role))),
        footer=DivFullySpaced(
            DivHStacked(UkIcon("map-pin", height=16), P(location)),
            DivHStacked(*(UkIconLink(icon, height=16) for icon in icons))))
```

Forms are styled and spaced for you without significant additional
classes.

``` python
def MonsterForm():
    relationship = ["Parent",'Sibling', "Friend", "Spouse", "Significant Other", "Relative", "Child", "Other"]
    return Div(
        DivCentered(
            H3("Emergency Contact Form"),
            P("Please fill out the form completely", cls=TextPresets.muted_sm)),
        Form(
            Grid(LabelInput("Name",id='name'),LabelInput("Email",     id='email')),
            H3("Relationship to patient"),
            Grid(*[LabelCheckboxX(o) for o in relationship], cols=4, cls='space-y-3'),
            DivCentered(Button("Submit Form", cls=ButtonT.primary))),
        cls='space-y-4')
```

Text can be styled with markdown automatically with MonsterUI

```` python
render_md("""
# My Document

> Important note here

+ List item with **bold**
+ Another with `code`

```python
def hello():
    print("world")
```
""")
````

    '<div><h1 class="uk-h1 text-4xl font-bold mt-12 mb-6">My Document</h1>\n<blockquote class="uk-blockquote pl-4 border-l-4 border-primary italic mb-6">\n<p class="text-lg leading-relaxed mb-6">Important note here</p>\n</blockquote>\n<ul class="uk-list uk-list-bullet space-y-2 mb-6 ml-6 text-lg">\n<li class="leading-relaxed">List item with <strong>bold</strong></li>\n<li class="leading-relaxed">Another with <code class="uk-codespan px-1">code</code></li>\n</ul>\n<pre class="bg-base-200 rounded-lg p-4 mb-6"><code class="language-python uk-codespan px-1 uk-codespan px-1 block overflow-x-auto">def hello():\n    print("world")\n</code></pre>\n</div>'

Or using semantic HTML:

``` python
def SemanticText():
    return Card(
        H1("MonsterUI's Semantic Text"),
        P(
            Strong("MonsterUI"), " brings the power of semantic HTML to life with ",
            Em("beautiful styling"), " and ", Mark("zero configuration"), "."),
        Blockquote(
            P("Write semantic HTML in pure Python, get modern styling for free."),
            Cite("MonsterUI Team")),
        footer=Small("Released February 2025"),)
```</doc><doc title="HTMX reference" desc="Brief description of all HTMX attributes, CSS classes, headers, events, extensions, js lib methods, and config options">+++
title = "Reference"
+++

## Contents

* [htmx Core Attributes](#attributes)
* [htmx Additional Attributes](#attributes-additional)
* [htmx CSS Classes](#classes)
* [htmx Request Headers](#request_headers)
* [htmx Response Headers](#response_headers)
* [htmx Events](#events)
* [htmx Extensions](/extensions)
* [JavaScript API](#api)
* [Configuration Options](#config)

## Core Attribute Reference {#attributes}

The most common attributes when using htmx.

<div class="info-table">

| Attribute                                        | Description                                                                                                        |
|--------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|
| [`hx-get`](@/attributes/hx-get.md)               | issues a `GET` to the specified URL                                                                                |
| [`hx-post`](@/attributes/hx-post.md)             | issues a `POST` to the specified URL                                                                               |
| [`hx-on*`](@/attributes/hx-on.md)                | handle events with inline scripts on elements                                                                      |
| [`hx-push-url`](@/attributes/hx-push-url.md)     | push a URL into the browser location bar to create history                                                         |
| [`hx-select`](@/attributes/hx-select.md)         | select content to swap in from a response                                                                          |
| [`hx-select-oob`](@/attributes/hx-select-oob.md) | select content to swap in from a response, somewhere other than the target (out of band)                           |
| [`hx-swap`](@/attributes/hx-swap.md)             | controls how content will swap in (`outerHTML`, `beforeend`, `afterend`, ...)                                      |
| [`hx-swap-oob`](@/attributes/hx-swap-oob.md)     | mark element to swap in from a response (out of band)                                                              |
| [`hx-target`](@/attributes/hx-target.md)         | specifies the target element to be swapped                                                                         |
| [`hx-trigger`](@/attributes/hx-trigger.md)       | specifies the event that triggers the request                                                                      |
| [`hx-vals`](@/attributes/hx-vals.md)             | add values to submit with the request (JSON format)                                                                |

</div>

## Additional Attribute Reference {#attributes-additional}

All other attributes available in htmx.

<div class="info-table">

| Attribute                                            | Description                                                                                                                        |
|------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|
| [`hx-boost`](@/attributes/hx-boost.md)               | add [progressive enhancement](https://en.wikipedia.org/wiki/Progressive_enhancement) for links and forms                           |
| [`hx-confirm`](@/attributes/hx-confirm.md)           | shows a `confirm()` dialog before issuing a request                                                                                |
| [`hx-delete`](@/attributes/hx-delete.md)             | issues a `DELETE` to the specified URL                                                                                             |
| [`hx-disable`](@/attributes/hx-disable.md)           | disables htmx processing for the given node and any children nodes                                                                 |
| [`hx-disabled-elt`](@/attributes/hx-disabled-elt.md) | adds the `disabled` attribute to the specified elements while a request is in flight                                               |
| [`hx-disinherit`](@/attributes/hx-disinherit.md)     | control and disable automatic attribute inheritance for child nodes                                                                |
| [`hx-encoding`](@/attributes/hx-encoding.md)         | changes the request encoding type                                                                                                  |
| [`hx-ext`](@/attributes/hx-ext.md)                   | extensions to use for this element                                                                                                 |
| [`hx-headers`](@/attributes/hx-headers.md)           | adds to the headers that will be submitted with the request                                                                        |
| [`hx-history`](@/attributes/hx-history.md)           | prevent sensitive data being saved to the history cache                                                                            |
| [`hx-history-elt`](@/attributes/hx-history-elt.md)   | the element to snapshot and restore during history navigation                                                                      |
| [`hx-include`](@/attributes/hx-include.md)           | include additional data in requests                                                                                                |
| [`hx-indicator`](@/attributes/hx-indicator.md)       | the element to put the `htmx-request` class on during the request                                                                  |
| [`hx-inherit`](@/attributes/hx-inherit.md)           | control and enable automatic attribute inheritance for child nodes if it has been disabled by default                            |
| [`hx-params`](@/attributes/hx-params.md)             | filters the parameters that will be submitted with a request                                                                       |
| [`hx-patch`](@/attributes/hx-patch.md)               | issues a `PATCH` to the specified URL                                                                                              |
| [`hx-preserve`](@/attributes/hx-preserve.md)         | specifies elements to keep unchanged between requests                                                                              |
| [`hx-prompt`](@/attributes/hx-prompt.md)             | shows a `prompt()` before submitting a request                                                                                     |
| [`hx-put`](@/attributes/hx-put.md)                   | issues a `PUT` to the specified URL                                                                                                |
| [`hx-replace-url`](@/attributes/hx-replace-url.md)   | replace the URL in the browser location bar                                                                                        |
| [`hx-request`](@/attributes/hx-request.md)           | configures various aspects of the request                                                                                          |
| [`hx-sync`](@/attributes/hx-sync.md)                 | control how requests made by different elements are synchronized                                                                   |
| [`hx-validate`](@/attributes/hx-validate.md)         | force elements to validate themselves before a request                                                                             |
| [`hx-vars`](@/attributes/hx-vars.md)                 | adds values dynamically to the parameters to submit with the request (deprecated, please use [`hx-vals`](@/attributes/hx-vals.md)) |

</div>

## CSS Class Reference {#classes}

<div class="info-table">

| Class | Description |
|-----------|-------------|
| `htmx-added` | Applied to a new piece of content before it is swapped, removed after it is settled.
| `htmx-indicator` | A dynamically generated class that will toggle visible (opacity:1) when a `htmx-request` class is present
| `htmx-request` | Applied to either the element or the element specified with [`hx-indicator`](@/attributes/hx-indicator.md) while a request is ongoing
| `htmx-settling` | Applied to a target after content is swapped, removed after it is settled. The duration can be modified via [`hx-swap`](@/attributes/hx-swap.md).
| `htmx-swapping` | Applied to a target before any content is swapped, removed after it is swapped. The duration can be modified via [`hx-swap`](@/attributes/hx-swap.md).

</div>

## HTTP Header Reference {#headers}

### Request Headers Reference {#request_headers}

<div class="info-table">

| Header | Description |
|--------|-------------|
| `HX-Boosted` | indicates that the request is via an element using [hx-boost](@/attributes/hx-boost.md)
| `HX-Current-URL` | the current URL of the browser
| `HX-History-Restore-Request` | "true" if the request is for history restoration after a miss in the local history cache
| `HX-Prompt` | the user response to an [hx-prompt](@/attributes/hx-prompt.md)
| `HX-Request` | always "true"
| `HX-Target` | the `id` of the target element if it exists
| `HX-Trigger-Name` | the `name` of the triggered element if it exists
| `HX-Trigger` | the `id` of the triggered element if it exists

</div>

### Response Headers Reference {#response_headers}

<div class="info-table">

| Header                                               | Description |
|------------------------------------------------------|-------------|
| [`HX-Location`](@/headers/hx-location.md)            | allows you to do a client-side redirect that does not do a full page reload
| [`HX-Push-Url`](@/headers/hx-push-url.md)            | pushes a new url into the history stack
| [`HX-Redirect`](@/headers/hx-redirect.md)            | can be used to do a client-side redirect to a new location
| `HX-Refresh`                                         | if set to "true" the client-side will do a full refresh of the page
| [`HX-Replace-Url`](@/headers/hx-replace-url.md)      | replaces the current URL in the location bar
| `HX-Reswap`                                          | allows you to specify how the response will be swapped. See [hx-swap](@/attributes/hx-swap.md) for possible values
| `HX-Retarget`                                        | a CSS selector that updates the target of the content update to a different element on the page
| `HX-Reselect`                                        | a CSS selector that allows you to choose which part of the response is used to be swapped in. Overrides an existing [`hx-select`](@/attributes/hx-select.md) on the triggering element
| [`HX-Trigger`](@/headers/hx-trigger.md)              | allows you to trigger client-side events
| [`HX-Trigger-After-Settle`](@/headers/hx-trigger.md) | allows you to trigger client-side events after the settle step
| [`HX-Trigger-After-Swap`](@/headers/hx-trigger.md)   | allows you to trigger client-side events after the swap step

</div>

## Event Reference {#events}

<div class="info-table">

| Event | Description |
|-------|-------------|
| [`htmx:abort`](@/events.md#htmx:abort) | send this event to an element to abort a request
| [`htmx:afterOnLoad`](@/events.md#htmx:afterOnLoad) | triggered after an AJAX request has completed processing a successful response
| [`htmx:afterProcessNode`](@/events.md#htmx:afterProcessNode) | triggered after htmx has initialized a node
| [`htmx:afterRequest`](@/events.md#htmx:afterRequest)  | triggered after an AJAX request has completed
| [`htmx:afterSettle`](@/events.md#htmx:afterSettle)  | triggered after the DOM has settled
| [`htmx:afterSwap`](@/events.md#htmx:afterSwap)  | triggered after new content has been swapped in
| [`htmx:beforeCleanupElement`](@/events.md#htmx:beforeCleanupElement)  | triggered before htmx [disables](@/attributes/hx-disable.md) an element or removes it from the DOM
| [`htmx:beforeOnLoad`](@/events.md#htmx:beforeOnLoad)  | triggered before any response processing occurs
| [`htmx:beforeProcessNode`](@/events.md#htmx:beforeProcessNode) | triggered before htmx initializes a node
| [`htmx:beforeRequest`](@/events.md#htmx:beforeRequest)  | triggered before an AJAX request is made
| [`htmx:beforeSwap`](@/events.md#htmx:beforeSwap)  | triggered before a swap is done, allows you to configure the swap
| [`htmx:beforeSend`](@/events.md#htmx:beforeSend)  | triggered just before an ajax request is sent
| [`htmx:beforeTransition`](@/events.md#htmx:beforeTransition)  | triggered before the [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) wrapped swap occurs
| [`htmx:configRequest`](@/events.md#htmx:configRequest)  | triggered before the request, allows you to customize parameters, headers
| [`htmx:confirm`](@/events.md#htmx:confirm)  | triggered after a trigger occurs on an element, allows you to cancel (or delay) issuing the AJAX request
| [`htmx:historyCacheError`](@/events.md#htmx:historyCacheError)  | triggered on an error during cache writing
| [`htmx:historyCacheHit`](@/events.md#htmx:historyCacheHit)  | triggered on a cache hit in the history subsystem
| [`htmx:historyCacheMiss`](@/events.md#htmx:historyCacheMiss)  | triggered on a cache miss in the history subsystem
| [`htmx:historyCacheMissLoadError`](@/events.md#htmx:historyCacheMissLoadError)  | triggered on a unsuccessful remote retrieval
| [`htmx:historyCacheMissLoad`](@/events.md#htmx:historyCacheMissLoad)  | triggered on a successful remote retrieval
| [`htmx:historyRestore`](@/events.md#htmx:historyRestore)  | triggered when htmx handles a history restoration action
| [`htmx:beforeHistorySave`](@/events.md#htmx:beforeHistorySave)  | triggered before content is saved to the history cache
| [`htmx:load`](@/events.md#htmx:load)  | triggered when new content is added to the DOM
| [`htmx:noSSESourceError`](@/events.md#htmx:noSSESourceError)  | triggered when an element refers to a SSE event in its trigger, but no parent SSE source has been defined
| [`htmx:onLoadError`](@/events.md#htmx:onLoadError)  | triggered when an exception occurs during the onLoad handling in htmx
| [`htmx:oobAfterSwap`](@/events.md#htmx:oobAfterSwap)  | triggered after an out of band element as been swapped in
| [`htmx:oobBeforeSwap`](@/events.md#htmx:oobBeforeSwap)  | triggered before an out of band element swap is done, allows you to configure the swap
| [`htmx:oobErrorNoTarget`](@/events.md#htmx:oobErrorNoTarget)  | triggered when an out of band element does not have a matching ID in the current DOM
| [`htmx:prompt`](@/events.md#htmx:prompt)  | triggered after a prompt is shown
| [`htmx:pushedIntoHistory`](@/events.md#htmx:pushedIntoHistory)  | triggered after a url is pushed into history
| [`htmx:replacedInHistory`](@/events.md#htmx:replacedInHistory)  | triggered after a url is replaced in history
| [`htmx:responseError`](@/events.md#htmx:responseError)  | triggered when an HTTP response error (non-`200` or `300` response code) occurs
| [`htmx:sendAbort`](@/events.md#htmx:sendAbort)  | triggered when a request is aborted
| [`htmx:sendError`](@/events.md#htmx:sendError)  | triggered when a network error prevents an HTTP request from happening
| [`htmx:sseError`](@/events.md#htmx:sseError)  | triggered when an error occurs with a SSE source
| [`htmx:sseOpen`](/events#htmx:sseOpen)  | triggered when a SSE source is opened
| [`htmx:swapError`](@/events.md#htmx:swapError)  | triggered when an error occurs during the swap phase
| [`htmx:targetError`](@/events.md#htmx:targetError)  | triggered when an invalid target is specified
| [`htmx:timeout`](@/events.md#htmx:timeout)  | triggered when a request timeout occurs
| [`htmx:validation:validate`](@/events.md#htmx:validation:validate)  | triggered before an element is validated
| [`htmx:validation:failed`](@/events.md#htmx:validation:failed)  | triggered when an element fails validation
| [`htmx:validation:halted`](@/events.md#htmx:validation:halted)  | triggered when a request is halted due to validation errors
| [`htmx:xhr:abort`](@/events.md#htmx:xhr:abort)  | triggered when an ajax request aborts
| [`htmx:xhr:loadend`](@/events.md#htmx:xhr:loadend)  | triggered when an ajax request ends
| [`htmx:xhr:loadstart`](@/events.md#htmx:xhr:loadstart)  | triggered when an ajax request starts
| [`htmx:xhr:progress`](@/events.md#htmx:xhr:progress)  | triggered periodically during an ajax request that supports progress events

</div>

## JavaScript API Reference {#api}

<div class="info-table">

| Method | Description |
|-------|-------------|
| [`htmx.addClass()`](@/api.md#addClass)  | Adds a class to the given element
| [`htmx.ajax()`](@/api.md#ajax)  | Issues an htmx-style ajax request
| [`htmx.closest()`](@/api.md#closest)  | Finds the closest parent to the given element matching the selector
| [`htmx.config`](@/api.md#config)  | A property that holds the current htmx config object
| [`htmx.createEventSource`](@/api.md#createEventSource)  | A property holding the function to create SSE EventSource objects for htmx
| [`htmx.createWebSocket`](@/api.md#createWebSocket)  | A property holding the function to create WebSocket objects for htmx
| [`htmx.defineExtension()`](@/api.md#defineExtension)  | Defines an htmx [extension](https://htmx.org/extensions)
| [`htmx.find()`](@/api.md#find)  | Finds a single element matching the selector
| [`htmx.findAll()` `htmx.findAll(elt, selector)`](@/api.md#find)  | Finds all elements matching a given selector
| [`htmx.logAll()`](@/api.md#logAll)  | Installs a logger that will log all htmx events
| [`htmx.logger`](@/api.md#logger)  | A property set to the current logger (default is `null`)
| [`htmx.off()`](@/api.md#off)  | Removes an event listener from the given element
| [`htmx.on()`](@/api.md#on)  | Creates an event listener on the given element, returning it
| [`htmx.onLoad()`](@/api.md#onLoad)  | Adds a callback handler for the `htmx:load` event
| [`htmx.parseInterval()`](@/api.md#parseInterval)  | Parses an interval declaration into a millisecond value
| [`htmx.process()`](@/api.md#process)  | Processes the given element and its children, hooking up any htmx behavior
| [`htmx.remove()`](@/api.md#remove)  | Removes the given element
| [`htmx.removeClass()`](@/api.md#removeClass)  | Removes a class from the given element
| [`htmx.removeExtension()`](@/api.md#removeExtension)  | Removes an htmx [extension](https://htmx.org/extensions)
| [`htmx.swap()`](@/api.md#swap)  | Performs swapping (and settling) of HTML content
| [`htmx.takeClass()`](@/api.md#takeClass)  | Takes a class from other elements for the given element
| [`htmx.toggleClass()`](@/api.md#toggleClass)  | Toggles a class from the given element
| [`htmx.trigger()`](@/api.md#trigger)  | Triggers an event on an element
| [`htmx.values()`](@/api.md#values)  | Returns the input values associated with the given element

</div>


## Configuration Reference {#config}

Htmx has some configuration options that can be accessed either programmatically or declaratively.  They are
listed below:

<div class="info-table">

| Config Variable                        | Info                                                                                                                                                                       |
|----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `htmx.config.historyEnabled`           | defaults to `true`, really only useful for testing                                                                                                                         |
| `htmx.config.historyCacheSize`         | defaults to 10                                                                                                                                                             |
| `htmx.config.refreshOnHistoryMiss`     | defaults to `false`, if set to `true` htmx will issue a full page refresh on history misses rather than use an AJAX request                                                |
| `htmx.config.defaultSwapStyle`         | defaults to `innerHTML`                                                                                                                                                    |
| `htmx.config.defaultSwapDelay`         | defaults to 0                                                                                                                                                              |
| `htmx.config.defaultSettleDelay`       | defaults to 20                                                                                                                                                             |
| `htmx.config.includeIndicatorStyles`   | defaults to `true` (determines if the indicator styles are loaded)                                                                                                         |
| `htmx.config.indicatorClass`           | defaults to `htmx-indicator`                                                                                                                                               |
| `htmx.config.requestClass`             | defaults to `htmx-request`                                                                                                                                                 |
| `htmx.config.addedClass`               | defaults to `htmx-added`                                                                                                                                                   |
| `htmx.config.settlingClass`            | defaults to `htmx-settling`                                                                                                                                                |
| `htmx.config.swappingClass`            | defaults to `htmx-swapping`                                                                                                                                                |
| `htmx.config.allowEval`                | defaults to `true`, can be used to disable htmx's use of eval for certain features (e.g. trigger filters)                                                                  |
| `htmx.config.allowScriptTags`          | defaults to `true`, determines if htmx will process script tags found in new content                                                                                       |
| `htmx.config.inlineScriptNonce`        | defaults to `''`, meaning that no nonce will be added to inline scripts                                                                                                    |
| `htmx.config.inlineStyleNonce`         | defaults to `''`, meaning that no nonce will be added to inline styles                                                                                                     |
| `htmx.config.attributesToSettle`       | defaults to `["class", "style", "width", "height"]`, the attributes to settle during the settling phase                                                                    |
| `htmx.config.wsReconnectDelay`         | defaults to `full-jitter`                                                                                                                                                  |
| `htmx.config.wsBinaryType`             | defaults to `blob`, the [the type of binary data](https://developer.mozilla.org/docs/Web/API/WebSocket/binaryType) being received over the WebSocket connection            |
| `htmx.config.disableSelector`          | defaults to `[hx-disable], [data-hx-disable]`, htmx will not process elements with this attribute on it or a parent                                                        |
| `htmx.config.disableInheritance`       | defaults to `false`. If it is set to `true`, the inheritance of attributes is completely disabled and you can explicitly specify the inheritance with the [hx-inherit](@/attributes/hx-inherit.md) attribute.
| `htmx.config.withCredentials`          | defaults to `false`, allow cross-site Access-Control requests using credentials such as cookies, authorization headers or TLS client certificates                          |
| `htmx.config.timeout`                  | defaults to 0, the number of milliseconds a request can take before automatically being terminated                                                                         |
| `htmx.config.scrollBehavior`           | defaults to 'instant', the scroll behavior when using the [show](@/attributes/hx-swap.md#scrolling-scroll-show) modifier with `hx-swap`. The allowed values are `instant` (scrolling should happen instantly in a single jump), `smooth` (scrolling should animate smoothly) and `auto` (scroll behavior is determined by the computed value of [scroll-behavior](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior)). |
| `htmx.config.defaultFocusScroll`       | if the focused element should be scrolled into view, defaults to false and can be overridden using the [focus-scroll](@/attributes/hx-swap.md#focus-scroll) swap modifier. |
| `htmx.config.getCacheBusterParam`      | defaults to false, if set to true htmx will append the target element to the `GET` request in the format `org.htmx.cache-buster=targetElementId`                           |
| `htmx.config.globalViewTransitions`    | if set to `true`, htmx will use the [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) API when swapping in new content.             |
| `htmx.config.methodsThatUseUrlParams`  | defaults to `["get", "delete"]`, htmx will format requests with these methods by encoding their parameters in the URL, not the request body                                |
| `htmx.config.selfRequestsOnly`         | defaults to `true`, whether to only allow AJAX requests to the same domain as the current document                                                             |
| `htmx.config.ignoreTitle`              | defaults to `false`, if set to `true` htmx will not update the title of the document when a `title` tag is found in new content                                            |
| `htmx.config.scrollIntoViewOnBoost`    | defaults to `true`, whether or not the target of a boosted element is scrolled into the viewport. If `hx-target` is omitted on a boosted element, the target defaults to `body`, causing the page to scroll to the top. |
| `htmx.config.triggerSpecsCache`        | defaults to `null`, the cache to store evaluated trigger specifications into, improving parsing performance at the cost of more memory usage. You may define a simple object to use a never-clearing cache, or implement your own system using a [proxy object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy) |
| `htmx.config.responseHandling`         | the default [Response Handling](@/docs.md#response-handling) behavior for response status codes can be configured here to either swap or error                             |
| `htmx.config.allowNestedOobSwaps`      | defaults to `true`, whether to process OOB swaps on elements that are nested within the main response element. See [Nested OOB Swaps](@/attributes/hx-swap-oob.md#nested-oob-swaps). |
| `htmx.config.historyRestoreAsHxRequest`| defaults to `true`, Whether to treat history cache miss full page reload requests as a "HX-Request" by returning this response header. This should always be disabled when using HX-Request header to optionally return partial responses                                                                                                         |


</div>

You can set them directly in javascript, or you can use a `meta` tag:

```html
<meta name="htmx-config" content='{"defaultSwapStyle":"outerHTML"}'>
```</doc><doc title="Starlette quick guide" desc="A quick overview of some Starlette features useful to FastHTML devs."># 🌟 Starlette Quick Manual


2020-02-09

Starlette is the ASGI web framework used as the foundation of FastHTML. Listed here are some Starlette features FastHTML developers can use directly, since the `FastHTML` class inherits from the `Starlette` class (but note that FastHTML has its own customised `RouteX` and `RouterX` classes for routing, to handle FT element trees etc).

## Get uploaded file content

```
async def handler(request):
    inp = await request.form()
    uploaded_file = inp["filename"]
    filename = uploaded_file.filename           # abc.png
    content_type = uploaded.content_type    # MIME type, e.g. image/png
    content = await uploaded_file.read()       # image content

```

## Return a customized response (status code and headers)

```
import json
from starlette.responses import Response

async def handler(request):
    data = {
        "name": "Bo"
    }
    return Response(json.dumps(data), media_type="application/json")

```

`Response` takes `status_code`, `headers` and `media_type`, so if we want to change a response's status code, we can do:

```
return Response(content, statu_code=404)

```

And customized headers:

```
headers = {
	"x-extra-key": "value"
}
return Response(content, status_code=200, headers=headers)

```

## Redirect

```
from starlette.responses import RedirectResponse

async handler(request):
    # Customize status_code: 
    #   301: permanent redirect 
    #   302: temporary redirect 
    #   303: see others
    #   307: temporary redirect (default)
    return RedirectResponse(url=url, status_code=303)

```

## Request context

### URL Object: `request.url`

  * Get request full url: `url = str(request.url)`
  * Get scheme: `request.url.scheme` (http, https, ws, wss)
  * Get netloc: `request.url.netloc`, e.g.: example.com:8080
  * Get path: `request.url.path`, e.g.: /search
  * Get query string: `request.url.query`, e.g.: kw=hello
  * Get hostname: `request.url.hostname`, e.g.: example.com
  * Get port: `request.url.port`, e.g.: 8080
  * If using secure scheme: `request.url.is_secure`, True is schme is `https` or `wss`

### Headers: `request.headers`

```
{
    'host': 'example.com:8080', 
    'connection': 'keep-alive', 
    'cache-control': 'max-age=0', 
    'sec-ch-ua': 'Google Chrome 80', 
    'dnt': '1', 
    'upgrade-insecure-requests': '1', 
    'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) ...',
    'sec-fetch-dest': 'document', 
    'accept': 'text/html,image/apng,*/*;q=0.8;v=b3;q=0.9', 
    'sec-origin-policy': '0', 
    'sec-fetch-site': 'none', 
    'sec-fetch-mode': 'navigate', 
    'sec-fetch-user': '?1', 
    'accept-encoding': 'gzip, deflate, br', 
    'accept-language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,zh-TW;q=0.6', 
    'cookie': 'session=eyJhZG1pbl91c2_KiQ...'
}

```

### Client: `request.client`

  * `request.client.host`: get client sock IP
  * `request.client.port`: get client sock port

### Method: `request.method`

  * `request.method`: GET, POST, etc.

### Get Data

  * `await request.body()`: get raw data from body
  * `await request.json()`: get passed data and parse it as JSON
  * `await request.form()`: get posted data and pass it as dictionary

### Scope: `request.scope`

```
{
    'type': 'http', 
    'http_version': '1.1', 
    'server': ('127.0.0.1', 9092), 
    'client': ('127.0.0.1', 53102), 
    'scheme': 'https', 
    'method': 'GET', 
    'root_path': '', 
    'path': '/', 
    'raw_path': b'/', 
    'query_string': b'kw=hello', 
    'headers': [
        (b'host', b'example.com:8080'), 
        (b'connection', b'keep-alive'), 
        (b'cache-control', b'max-age=0'), 
        ...
    ], 
    'app': <starlette.applications.Starlette object at 0x1081bd650>, 
    'session': {'uid': '57ba03ea7333f72a25f837cf'}, 
    'router': <starlette.routing.Router object at 0x1081bd6d0>, 
    'endpoint': <class 'app.index.Index'>, 
    'path_params': {}
}

```

## Put varaible in request & app scope

```
app.state.dbconn = get_db_conn()
request.state.start_time = time.time()
# use app-scope state variable in a request
request.app.state.dbconn

```

## Utility functions

### Use `State` to wrap a dictionary

```
from starlette.datastructures import State

data = {
    "name": "Bo"
}
print(data["name"])
# now wrap it with State function
wrapped = State(data)
# You can use the dot syntaxt, but can't use `wrapped["name"]` any more.
print(wrapped.name)

```

### login_required wrapper function

NB: This is easier to do in FastHTML using Beforeware.

```
import functools
from starlette.endpoints import HTTPEndpoint
from starlette.responses import Response

def login_required(login_url="/signin"):
    def decorator(handler):
        @functools.wraps(handler)
        async def new_handler(obj, req, *args, **kwargs):
            user = req.session.get("login_user")
            if user is None:
                return seeother(login_url)
            return await handler(obj, req, *args, **kwargs)
        return new_handler
    return decorator

class MyAccount(HTTPEndpiont):
    @login_required()
    async def get(self, request):
        # some logic here
        content = "hello"
        return Response(content)

```

## Exceptions

Handle exception and customize 403, 404, 503, 500 page:

```
from starlette.exceptions import HTTPException

async def exc_handle_403(request, exc):
    return HTMLResponse("My 403 page", status_code=exc.status_code)

async def exc_handle_404(request, exc):
    return HTMLResponse("My 404 page", status_code=exc.status_code)

async def exc_handle_503(request, exc):
    return HTMLResponse("Failed, please try it later", status_code=exc.status_code)

# error is not exception, 500 is server side unexpected error, all other status code will be treated as Exception
async def err_handle_500(request, exc):
    import traceback
    Log.error(traceback.format_exc())
    return HTMLResponse("My 500 page", status_code=500)

# To add handler, we can add either status_code or Exception itself as key
exception_handlers = {
    403: exc_handle_403,
    404: exc_handle_404,
    503: exc_handle_503,
    500: err_handle_500,
    #HTTPException: exc_handle_500,
}

app = Starlette(routes=routes, exception_handlers=exception_handlers)

```

## Background Task

### Put some async task as background task

```
import aiofiles
from starlette.background import BackgroundTask
from starlette.responses import Response

aiofiles_remove = aiofiles.os.wrap(os.remove)

async def del_file(fpath):
    await aiofiles_remove(fpath)

async def handler(request):
    content = ""
    fpath = "/tmp/tmpfile.txt"
    task = BackgroundTask(del_file, fpath=fpath)
    return Response(content, background=task)

```

### Put multiple tasks as background task

```
from starlette.background import BackgroundTasks

async def task1(name):
    pass

async def task2(email):
    pass

async def handler(request):
    tasks = BackgroundTasks()
    tasks.add_task(task1, name="John")
    tasks.add_task(task2, email="info@example.com")
    content = ""
    return Response(content, background=tasks)

```

## Write middleware

There are 2 ways to write middleware:

### Define `__call__` function:

```
class MyMiddleware:
    def __init__(self, app):
        self.app = app

    async def __call__(self, scope, receive, send):
        # see above scope dictionary as reference
        headers = dict(scope["headers"])
        # do something
        # pass to next middleware
        return await self.app(scope, receive, send)

```

### Use `BaseHTTPMiddleware`

```
from starlette.middleware.base import BaseHTTPMiddleware

class CustomHeaderMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        # do something before pass to next middleware
        response = await call_next(request)
        # do something after next middleware returned
        response.headers['X-Author'] = 'John'
        return response

```</doc></docs><api><doc title="API List" desc="A succint list of all functions and methods in fasthtml."># fasthtml Module Documentation

## fasthtml.authmw

- `class BasicAuthMiddleware`
    - `def __init__(self, app, cb, skip)`
    - `def __call__(self, scope, receive, send)`
    - `def authenticate(self, conn)`

## fasthtml.cli

- `@call_parse def railway_link()`
    Link the current directory to the current project's Railway service

- `@call_parse def railway_deploy(name, mount)`
    Deploy a FastHTML app to Railway

## fasthtml.components

> `ft_html` and `ft_hx` functions to add some conveniences to `ft`, along with a full set of basic HTML components, and functions to work with forms and `FT` conversion

- `def File(fname)`
    Use the unescaped text in file `fname` directly

- `def show(ft, *rest)`
    Renders FT Components into HTML within a Jupyter notebook.

- `def fill_form(form, obj)`
    Fills named items in `form` using attributes in `obj`

- `def fill_dataclass(src, dest)`
    Modifies dataclass in-place and returns it

- `def find_inputs(e, tags, **kw)`
    Recursively find all elements in `e` with `tags` and attrs matching `kw`

- `def html2ft(html, attr1st)`
    Convert HTML to an `ft` expression

- `def sse_message(elm, event)`
    Convert element `elm` into a format suitable for SSE streaming

## fasthtml.core

> The `FastHTML` subclass of `Starlette`, along with the `RouterX` and `RouteX` classes it automatically uses.

- `def parsed_date(s)`
    Convert `s` to a datetime

- `def snake2hyphens(s)`
    Convert `s` from snake case to hyphenated and capitalised

- `@dataclass class HtmxHeaders`
    - `def __bool__(self)`
    - `def __init__(self, boosted, current_url, history_restore_request, prompt, request, target, trigger_name, trigger)`

- `@dataclass class HttpHeader`
    - `def __init__(self, k, v)`

- `@use_kwargs_dict(**htmx_resps) def HtmxResponseHeaders(**kwargs)`
    HTMX response headers

- `def form2dict(form)`
    Convert starlette form data to a dict

- `def parse_form(req)`
    Starlette errors on empty multipart forms, so this checks for that situation

- `class JSONResponse`
    Same as starlette's version, but auto-stringifies non serializable types

    - `def render(self, content)`

- `def flat_xt(lst)`
    Flatten lists

- `class Beforeware`
    - `def __init__(self, f, skip)`

- `def EventStream(s)`
    Create a text/event-stream response from `s`

- `def flat_tuple(o)`
    Flatten lists

- `def noop_body(c, req)`
    Default Body wrap function which just returns the content

- `def respond(req, heads, bdy)`
    Default FT response creation function

- `class Redirect`
    Use HTMX or Starlette RedirectResponse as required to redirect to `loc`

    - `def __init__(self, loc)`
    - `def __response__(self, req)`

- `def qp(p, **kw)`
    Add parameters kw to path p

- `def def_hdrs(htmx, surreal)`
    Default headers for a FastHTML app

- `class FastHTML`
    - `def __init__(self, debug, routes, middleware, title, exception_handlers, on_startup, on_shutdown, lifespan, hdrs, ftrs, exts, before, after, surreal, htmx, default_hdrs, sess_cls, secret_key, session_cookie, max_age, sess_path, same_site, sess_https_only, sess_domain, key_fname, body_wrap, htmlkw, nb_hdrs, canonical, **bodykw)`

- `@patch def ws(self, path, conn, disconn, name, middleware)`
    Add a websocket route at `path`

- `def nested_name(f)`
    Get name of function `f` using '_' to join nested function names

- `@patch def route(self, path, methods, name, include_in_schema, body_wrap)`
    Add a route at `path`

- `def serve(appname, app, host, port, reload, reload_includes, reload_excludes)`
    Run the app in an async server, with live reload set as the default.

- `class Client`
    A simple httpx ASGI client that doesn't require `async`

    - `def __init__(self, app, url)`

- `class RouteFuncs`
    - `def __init__(self)`
    - `def __setattr__(self, name, value)`
    - `def __getattr__(self, name)`
    - `def __dir__(self)`

- `class APIRouter`
    Add routes to an app

    - `def __init__(self, prefix, body_wrap)`
    - `def __call__(self, path, methods, name, include_in_schema, body_wrap)`
        Add a route at `path`

    - `def __getattr__(self, name)`
    - `def to_app(self, app)`
        Add routes to `app`

    - `def ws(self, path, conn, disconn, name, middleware)`
        Add a websocket route at `path`


- `def cookie(key, value, max_age, expires, path, domain, secure, httponly, samesite)`
    Create a 'set-cookie' `HttpHeader`

- `@patch def static_route_exts(self, prefix, static_path, exts)`
    Add a static route at URL path `prefix` with files from `static_path` and `exts` defined by `reg_re_param()`

- `@patch def static_route(self, ext, prefix, static_path)`
    Add a static route at URL path `prefix` with files from `static_path` and single `ext` (including the '.')

- `class MiddlewareBase`
    - `def __call__(self, scope, receive, send)`

- `class FtResponse`
    Wrap an FT response with any Starlette `Response`

    - `def __init__(self, content, status_code, headers, cls, media_type, background)`
    - `def __response__(self, req)`

## fasthtml.fastapp

> The `fast_app` convenience wrapper

- `def fast_app(db_file, render, hdrs, ftrs, tbls, before, middleware, live, debug, title, routes, exception_handlers, on_startup, on_shutdown, lifespan, default_hdrs, pico, surreal, htmx, exts, canonical, secret_key, key_fname, session_cookie, max_age, sess_path, same_site, sess_https_only, sess_domain, htmlkw, bodykw, reload_attempts, reload_interval, static_path, body_wrap, nb_hdrs, **kwargs)`
    Create a FastHTML or FastHTMLWithLiveReload app.

## fasthtml.js

> Basic external Javascript lib wrappers

- `def light_media(css)`
    Render light media for day mode views

- `def dark_media(css)`
    Render dark media for night mode views

- `def MarkdownJS(sel)`
    Implements browser-based markdown rendering.

- `def HighlightJS(sel, langs, light, dark)`
    Implements browser-based syntax highlighting. Usage example [here](/tutorials/quickstart_for_web_devs.html#code-highlighting).

- `def MermaidJS(sel, theme)`
    Implements browser-based Mermaid diagram rendering.

## fasthtml.jupyter

> Use FastHTML in Jupyter notebooks

- `def nb_serve(app, log_level, port, host, **kwargs)`
    Start a Jupyter compatible uvicorn server with ASGI `app` on `port` with `log_level`

- `def nb_serve_async(app, log_level, port, host, **kwargs)`
    Async version of `nb_serve`

- `def is_port_free(port, host)`
    Check if `port` is free on `host`

- `def wait_port_free(port, host, max_wait)`
    Wait for `port` to be free on `host`

- `class JupyUvi`
    Start and stop a Jupyter compatible uvicorn server with ASGI `app` on `port` with `log_level`

    - `def __init__(self, app, log_level, host, port, start, **kwargs)`
    - `def start(self)`
    - `def start_async(self)`
    - `def stop(self)`

- `class JupyUviAsync`
    Start and stop an async Jupyter compatible uvicorn server with ASGI `app` on `port` with `log_level`

    - `def __init__(self, app, log_level, host, port, **kwargs)`
    - `def start(self)`
    - `def stop(self)`

- `def HTMX(path, host, app, port, height, link, iframe)`
    An iframe which displays the HTMX application in a notebook.

## fasthtml.live_reload

- `class FastHTMLWithLiveReload`
    `FastHTMLWithLiveReload` enables live reloading.
    This means that any code changes saved on the server will automatically
    trigger a reload of both the server and browser window.

    How does it work?
      - a websocket is created at `/live-reload`
      - a small js snippet `LIVE_RELOAD_SCRIPT` is injected into each webpage
      - this snippet connects to the websocket at `/live-reload` and listens for an `onclose` event
      - when the `onclose` event is detected the browser is reloaded

    Why do we listen for an `onclose` event?
      When code changes are saved the server automatically reloads if the --reload flag is set.
      The server reload kills the websocket connection. The `onclose` event serves as a proxy
      for "developer has saved some changes".

    Usage
        >>> from fasthtml.common import *
        >>> app = FastHTMLWithLiveReload()

        Run:
            serve()

    - `def __init__(self, *args, **kwargs)`

## fasthtml.oauth

> Basic scaffolding for handling OAuth

- `class GoogleAppClient`
    A `WebApplicationClient` for Google oauth2

    - `def __init__(self, client_id, client_secret, code, scope, project_id, **kwargs)`
    - `@classmethod def from_file(cls, fname, code, scope, **kwargs)`

- `class GitHubAppClient`
    A `WebApplicationClient` for GitHub oauth2

    - `def __init__(self, client_id, client_secret, code, scope, **kwargs)`

- `class HuggingFaceClient`
    A `WebApplicationClient` for HuggingFace oauth2

    - `def __init__(self, client_id, client_secret, code, scope, state, **kwargs)`

- `class DiscordAppClient`
    A `WebApplicationClient` for Discord oauth2

    - `def __init__(self, client_id, client_secret, is_user, perms, scope, **kwargs)`
    - `def login_link(self, redirect_uri, scope, state)`
    - `def parse_response(self, code, redirect_uri)`

- `class Auth0AppClient`
    A `WebApplicationClient` for Auth0 OAuth2

    - `def __init__(self, domain, client_id, client_secret, code, scope, redirect_uri, **kwargs)`
    - `def login_link(self, req)`

- `@patch def login_link(self, redirect_uri, scope, state, **kwargs)`
    Get a login link for this client

- `def redir_url(request, redir_path, scheme)`
    Get the redir url for the host in `request`

- `@patch def parse_response(self, code, redirect_uri)`
    Get the token from the oauth2 server response

- `@patch def get_info(self, token)`
    Get the info for authenticated user

- `@patch def retr_info(self, code, redirect_uri)`
    Combines `parse_response` and `get_info`

- `@patch def retr_id(self, code, redirect_uri)`
    Call `retr_info` and then return id/subscriber value

- `class OAuth`
    - `def __init__(self, app, cli, skip, redir_path, error_path, logout_path, login_path, https, http_patterns)`
    - `def redir_login(self, session)`
    - `def redir_url(self, req)`
    - `def login_link(self, req, scope, state)`
    - `def check_invalid(self, req, session, auth)`
    - `def logout(self, session)`
    - `def get_auth(self, info, ident, session, state)`

- `@patch() def consent_url(self, proj)`
    Get Google OAuth consent screen URL

- `@patch def save(self, fname)`
    Save credentials to `fname`

- `def load_creds(fname)`
    Load credentials from `fname`

- `@patch def creds(self)`
    Create `Credentials` from the client, refreshing if needed

## fasthtml.pico

> Basic components for generating Pico CSS tags

- `@delegates(ft_hx, keep=True) def Card(*c, **kwargs)`
    A PicoCSS Card, implemented as an Article with optional Header and Footer

- `@delegates(ft_hx, keep=True) def Group(*c, **kwargs)`
    A PicoCSS Group, implemented as a Fieldset with role 'group'

- `@delegates(ft_hx, keep=True) def Search(*c, **kwargs)`
    A PicoCSS Search, implemented as a Form with role 'search'

- `@delegates(ft_hx, keep=True) def Grid(*c, **kwargs)`
    A PicoCSS Grid, implemented as child Divs in a Div with class 'grid'

- `@delegates(ft_hx, keep=True) def DialogX(*c, **kwargs)`
    A PicoCSS Dialog, with children inside a Card

- `@delegates(ft_hx, keep=True) def Container(*args, **kwargs)`
    A PicoCSS Container, implemented as a Main with class 'container'

## fasthtml.stripe_otp

- `def create_price(app_nm, amt, currency)`
    Create a product and bind it to a price object. If product already exist just return the price list.

- `def archive_price(app_nm)`
    Archive a price - useful for cleanup if testing.

- `class Payment`

## fasthtml.svg

> Simple SVG FT elements

- `def Svg(*args, **kwargs)`
    An SVG tag; xmlns is added automatically, and viewBox defaults to height and width if not provided

- `@delegates(ft_hx) def ft_svg(tag, *c, **kwargs)`
    Create a standard `FT` element with some SVG-specific attrs

- `@delegates(ft_svg) def Rect(width, height, x, y, fill, stroke, stroke_width, rx, ry, **kwargs)`
    A standard SVG `rect` element

- `@delegates(ft_svg) def Circle(r, cx, cy, fill, stroke, stroke_width, **kwargs)`
    A standard SVG `circle` element

- `@delegates(ft_svg) def Ellipse(rx, ry, cx, cy, fill, stroke, stroke_width, **kwargs)`
    A standard SVG `ellipse` element

- `def transformd(translate, scale, rotate, skewX, skewY, matrix)`
    Create an SVG `transform` kwarg dict

- `@delegates(ft_svg) def Line(x1, y1, x2, y2, stroke, w, stroke_width, **kwargs)`
    A standard SVG `line` element

- `@delegates(ft_svg) def Polyline(*args, **kwargs)`
    A standard SVG `polyline` element

- `@delegates(ft_svg) def Polygon(*args, **kwargs)`
    A standard SVG `polygon` element

- `@delegates(ft_svg) def Text(*args, **kwargs)`
    A standard SVG `text` element

- `class PathFT`
    - `def M(self, x, y)`
        Move to.

    - `def L(self, x, y)`
        Line to.

    - `def H(self, x)`
        Horizontal line to.

    - `def V(self, y)`
        Vertical line to.

    - `def Z(self)`
        Close path.

    - `def C(self, x1, y1, x2, y2, x, y)`
        Cubic Bézier curve.

    - `def S(self, x2, y2, x, y)`
        Smooth cubic Bézier curve.

    - `def Q(self, x1, y1, x, y)`
        Quadratic Bézier curve.

    - `def T(self, x, y)`
        Smooth quadratic Bézier curve.

    - `def A(self, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y)`
        Elliptical Arc.


- `def SvgOob(*args, **kwargs)`
    Wraps an SVG shape as required for an HTMX OOB swap

- `def SvgInb(*args, **kwargs)`
    Wraps an SVG shape as required for an HTMX inband swap

## fasthtml.xtend

> Simple extensions to standard HTML components, such as adding sensible defaults

- `@delegates(ft_hx, keep=True) def A(*c, **kwargs)`
    An A tag; `href` defaults to '#' for more concise use with HTMX

- `@delegates(ft_hx, keep=True) def AX(txt, hx_get, target_id, hx_swap, href, **kwargs)`
    An A tag with just one text child, allowing hx_get, target_id, and hx_swap to be positional params

- `@delegates(ft_hx, keep=True) def Form(*c, **kwargs)`
    A Form tag; identical to plain `ft_hx` version except default `enctype='multipart/form-data'`

- `@delegates(ft_hx, keep=True) def Hidden(value, id, **kwargs)`
    An Input of type 'hidden'

- `@delegates(ft_hx, keep=True) def CheckboxX(checked, label, value, id, name, **kwargs)`
    A Checkbox optionally inside a Label, preceded by a `Hidden` with matching name

- `@delegates(ft_html, keep=True) def Script(code, **kwargs)`
    A Script tag that doesn't escape its code

- `@delegates(ft_html, keep=True) def Style(*c, **kwargs)`
    A Style tag that doesn't escape its code

- `def double_braces(s)`
    Convert single braces to double braces if next to special chars or newline

- `def undouble_braces(s)`
    Convert double braces to single braces if next to special chars or newline

- `def loose_format(s, **kw)`
    String format `s` using `kw`, without being strict about braces outside of template params

- `def ScriptX(fname, src, nomodule, type, _async, defer, charset, crossorigin, integrity, **kw)`
    A `script` element with contents read from `fname`

- `def replace_css_vars(css, pre, **kwargs)`
    Replace `var(--)` CSS variables with `kwargs` if name prefix matches `pre`

- `def StyleX(fname, **kw)`
    A `style` element with contents read from `fname` and variables replaced from `kw`

- `def Nbsp()`
    A non-breaking space

- `def Surreal(code)`
    Wrap `code` in `domReadyExecute` and set `m=me()` and `p=me('-')`

- `def On(code, event, sel, me)`
    An async surreal.js script block event handler for `event` on selector `sel,p`, making available parent `p`, event `ev`, and target `e`

- `def Prev(code, event)`
    An async surreal.js script block event handler for `event` on previous sibling, with same vars as `On`

- `def Now(code, sel)`
    An async surreal.js script block on selector `me(sel)`

- `def AnyNow(sel, code)`
    An async surreal.js script block on selector `any(sel)`

- `def run_js(js, id, **kw)`
    Run `js` script, auto-generating `id` based on name of caller if needed, and js-escaping any `kw` params

- `def jsd(org, repo, root, path, prov, typ, ver, esm, **kwargs)`
    jsdelivr `Script` or CSS `Link` tag, or URL

- `class Fragment`
    An empty tag, used as a container

    - `def __init__(self, *c)`

- `@delegates(ft_hx, keep=True) def Titled(title, *args, **kwargs)`
    An HTML partial containing a `Title`, and `H1`, and any provided children

- `def Socials(title, site_name, description, image, url, w, h, twitter_site, creator, card)`
    OG and Twitter social card headers

- `def YouTubeEmbed(video_id, **kwargs)`
    Embed a YouTube video

- `def Favicon(light_icon, dark_icon)`
    Light and dark favicon headers
</doc><doc title="MonsterUI API List" desc="Complete API Reference for Monster UI, a component framework similar to shadcn, but for FastHTML"># monsterui Module Documentation

## monsterui.core

- `class ThemeRadii(Enum)`
    Members: none, sm, md, lg


- `class ThemeShadows`

- `class ThemeFont`

- `class Theme(Enum)`
    Selector to choose theme and get all headers needed for app.  Includes frankenui + tailwind + daisyui + highlight.js options
    Members: slate, stone, gray, neutral, red, rose, orange, green, blue, yellow, violet, zinc

    - `headers(self, mode, icons, daisy, highlightjs, katex, apex_charts, radii, shadows, font)`
        Create frankenui and tailwind cdns

    - `local_headers(self, mode, static_dir, icons, daisy, highlightjs, katex, apex_charts, radii, shadows, font)`
        Create headers using local files downloaded from CDNs


## monsterui.daisy

- `class AlertT(Enum)`
    Alert styles from DaisyUI
    Members: info, success, warning, error


- `def Alert(*c, **kwargs)`
    Alert informs users about important events.

- `class StepsT(Enum)`
    Options for Steps
    Members: vertical, horizonal


- `class StepT(Enum)`
    Step styles for LiStep
    Members: primary, secondary, accent, info, success, warning, error, neutral


- `def Steps(*li, **kwargs)`
    Creates a steps container

- `def LiStep(*c, **kwargs)`
    Creates a step list item

- `class LoadingT(Enum)`
    Members: spinner, dots, ring, ball, bars, infinity, xs, sm, md, lg


- `def Loading(cls, htmx_indicator, **kwargs)`
    Creates a loading animation component

- `class ToastHT(Enum)`
    Horizontal position for Toast
    Members: start, center, end


- `class ToastVT(Enum)`
    Vertical position for Toast
    Members: top, middle, bottom


## monsterui.foundations

> Data Structures and Utilties

- `def stringify(o)`
    Converts input types into strings that can be passed to FT components

- `class VEnum(Enum)`
    Members: 

    - `__str__(self)`
    - `__add__(self, other)`
    - `__radd__(self, other)`

## monsterui.franken

- `class TextT(Enum)`
    Text Styles from https://franken-ui.dev/docs/text
    Members: paragraph, lead, meta, gray, italic, xs, sm, lg, xl, light, normal, medium, bold, extrabold, muted, primary, secondary, success, warning, error, info, left, right, center, justify, start, end, top, middle, bottom, truncate, break_, nowrap, underline, highlight


- `class TextPresets(Enum)`
    Common Typography Presets
    Members: muted_sm, muted_lg, bold_sm, bold_lg, md_weight_sm, md_weight_muted


- `def CodeSpan(*c, **kwargs)`
    A CodeSpan with Styling

- `def CodeBlock(*c, **kwargs)`
    CodeBlock with Styling

- `def H1(*c, **kwargs)`
    H1 with styling and appropriate size

- `def H2(*c, **kwargs)`
    H2 with styling and appropriate size

- `def H3(*c, **kwargs)`
    H3 with styling and appropriate size

- `def H4(*c, **kwargs)`
    H4 with styling and appropriate size

- `def H5(*c, **kwargs)`
    H5 with styling and appropriate size

- `def H6(*c, **kwargs)`
    H6 with styling and appropriate size

- `def Subtitle(*c, **kwargs)`
    Styled muted_sm text designed to go under Headings and Titles

- `def Q(*c, **kwargs)`
    Styled quotation mark

- `def Em(*c, **kwargs)`
    Styled emphasis text

- `def Strong(*c, **kwargs)`
    Styled strong text

- `def I(*c, **kwargs)`
    Styled italic text

- `def Small(*c, **kwargs)`
    Styled small text

- `def Mark(*c, **kwargs)`
    Styled highlighted text

- `def Del(*c, **kwargs)`
    Styled deleted text

- `def Ins(*c, **kwargs)`
    Styled inserted text

- `def Sub(*c, **kwargs)`
    Styled subscript text

- `def Sup(*c, **kwargs)`
    Styled superscript text

- `def Blockquote(*c, **kwargs)`
    Blockquote with Styling

- `def Caption(*c, **kwargs)`
    Styled caption text

- `def Cite(*c, **kwargs)`
    Styled citation text

- `def Time(*c, **kwargs)`
    Styled time element

- `def Address(*c, **kwargs)`
    Styled address element

- `def Abbr(*c, **kwargs)`
    Styled abbreviation with dotted underline

- `def Dfn(*c, **kwargs)`
    Styled definition term with italic and medium weight

- `def Kbd(*c, **kwargs)`
    Styled keyboard input with subtle background

- `def Samp(*c, **kwargs)`
    Styled sample output with subtle background

- `def Var(*c, **kwargs)`
    Styled variable with italic monospace

- `def Figure(*c, **kwargs)`
    Styled figure container with card-like appearance

- `def Details(*c, **kwargs)`
    Styled details element

- `def Summary(*c, **kwargs)`
    Styled summary element

- `def Data(*c, **kwargs)`
    Styled data element

- `def Meter(*c, **kwargs)`
    Styled meter element

- `def S(*c, **kwargs)`
    Styled strikethrough text (different semantic meaning from Del)

- `def U(*c, **kwargs)`
    Styled underline (for proper names in Chinese, proper spelling etc)

- `def Output(*c, **kwargs)`
    Styled output element for form results

- `def PicSumImg(h, w, id, grayscale, blur, **kwargs)`
    Creates a placeholder image using https://picsum.photos/

- `def AccordionItem(title, *c)`
    Creates a single item for use within an Accordion component, handling title, content, and open state.

- `def Accordion(*c, **kwargs)`
    Creates a styled Accordion container using accordion component.

- `class ButtonT(Enum)`
    Options for styling Buttons
    Members: default, ghost, primary, secondary, destructive, text, link, xs, sm, lg, xl, icon


- `def Button(*c, **kwargs)`
    Button with Styling (defaults to `submit` for form submission)

- `class ContainerT(Enum)`
    Max width container sizes from https://franken-ui.dev/docs/container
    Members: xs, sm, lg, xl, expand


- `class BackgroundT(Enum)`
    Members: muted, primary, secondary, default


- `def Container(*c, **kwargs)`
    Div to be used as a container that often wraps large sections or a page of content

- `def Titled(title, *c, **kwargs)`
    Creates a standard page structure for titled page.  Main(Container(title, content))

- `class DividerT(Enum)`
    Divider Styles from https://franken-ui.dev/docs/divider
    Members: icon, sm, vertical


- `def Divider(*c, **kwargs)`
    Divider with default styling and margin

- `def DividerSplit(*c)`
    Creates a simple horizontal line divider with configurable thickness and vertical spacing

- `def Article(*c, **kwargs)`
    A styled article container for blog posts or similar content

- `def ArticleTitle(*c, **kwargs)`
    A title component for use within an Article

- `def ArticleMeta(*c, **kwargs)`
    A metadata component for use within an Article showing things like date, author etc

- `class SectionT(Enum)`
    Section styles from https://franken-ui.dev/docs/section
    Members: default, muted, primary, secondary, xs, sm, lg, xl, remove_vertical


- `def Section(*c, **kwargs)`
    Section with styling and margins

- `def Form(*c, **kwargs)`
    A Form with default spacing between form elements

- `def Fieldset(*c, **kwargs)`
    A Fieldset with default styling

- `def Legend(*c, **kwargs)`
    A Legend with default styling

- `def Input(*c, **kwargs)`
    An Input with default styling

- `def Radio(*c, **kwargs)`
    A Radio with default styling

- `def CheckboxX(*c, **kwargs)`
    A Checkbox with default styling

- `def Range(*c, **kwargs)`
    A Range with default styling

- `def TextArea(*c, **kwargs)`
    A Textarea with default styling

- `def Switch(*c, **kwargs)`
    A Switch with default styling

- `def Upload(*c, **kwargs)`
    A file upload component with default styling

- `def UploadZone(*c, **kwargs)`
    A file drop zone component with default styling

- `def FormLabel(*c, **kwargs)`
    A Label with default styling

- `class LabelT(Enum)`
    Members: primary, secondary, destructive


- `def Label(*c, **kwargs)`
    FrankenUI labels, which look like pills

- `def UkFormSection(title, description, *c)`
    A form section with a title, description and optional button

- `def GenericLabelInput(label, lbl_cls, input_cls, container, cls, id, input_fn, **kwargs)`
    `Div(Label,Input)` component with Uk styling injected appropriately. Generally you should higher level API, such as `LabelInput` which is created for you in this library

- `def LabelInput(label, lbl_cls, input_cls, cls, id, **kwargs)`
    A `FormLabel` and `Input` pair that provides default spacing and links/names them based on id

- `def LabelRadio(label, lbl_cls, input_cls, container, cls, id, **kwargs)`
    A FormLabel and Radio pair that provides default spacing and links/names them based on id

- `def LabelCheckboxX(label, lbl_cls, input_cls, container, cls, id, **kwargs)`
    A FormLabel and CheckboxX pair that provides default spacing and links/names them based on id

- `def Options(*c)`
    Helper function to wrap things into `Option`s for use in `Select`

- `def Select(*option, **kwargs)`
    Creates a select dropdown with uk styling and option for adding a search box

- `def LabelSelect(*option, **kwargs)`
    A FormLabel and Select pair that provides default spacing and links/names them based on id

- `@delegates(GenericLabelInput, but=['input_fn', 'cls']) def LabelRange(label, lbl_cls, input_cls, cls, id, value, min, max, step, label_range, **kwargs)`
    A FormLabel and Range pair that provides default spacing and links/names them based on id

- `class AT(Enum)`
    Link styles from https://franken-ui.dev/docs/link
    Members: muted, text, reset, primary, classic


- `class ListT(Enum)`
    List styles using Tailwind CSS
    Members: disc, circle, square, decimal, hyphen, bullet, divider, striped


- `def ModalContainer(*c, **kwargs)`
    Creates a modal container that components go in

- `def ModalDialog(*c, **kwargs)`
    Creates a modal dialog

- `def ModalHeader(*c, **kwargs)`
    Creates a modal header

- `def ModalBody(*c, **kwargs)`
    Creates a modal body

- `def ModalFooter(*c, **kwargs)`
    Creates a modal footer

- `def ModalTitle(*c, **kwargs)`
    Creates a modal title

- `def ModalCloseButton(*c, **kwargs)`
    Creates a button that closes a modal with js

- `def Modal(*c, **kwargs)`
    Creates a modal with the appropriate classes to put the boilerplate in the appropriate places for you

- `def Placeholder(*c, **kwargs)`
    Creates a placeholder

- `def Progress(*c, **kwargs)`
    Creates a progress bar

- `def UkIcon(icon, height, width, stroke_width, cls, **kwargs)`
    Creates an icon using lucide icons

- `def UkIconLink(icon, height, width, stroke_width, cls, button, **kwargs)`
    Creates an icon link using lucide icons

- `def DiceBearAvatar(seed_name, h, w)`
    Creates an Avatar using https://dicebear.com/

- `def Center(*c, **kwargs)`
    Centers contents both vertically and horizontally by default

- `class FlexT(Enum)`
    Flexbox modifiers using Tailwind CSS
    Members: block, inline, left, center, right, between, around, stretch, top, middle, bottom, row, row_reverse, column, column_reverse, nowrap, wrap, wrap_reverse


- `def Grid(*div, **kwargs)`
    Creates a responsive grid layout with smart defaults based on content

- `def DivFullySpaced(*c, **kwargs)`
    Creates a flex div with it's components having as much space between them as possible

- `def DivCentered(*c, **kwargs)`
    Creates a flex div with it's components centered in it

- `def DivLAligned(*c, **kwargs)`
    Creates a flex div with it's components aligned to the left

- `def DivRAligned(*c, **kwargs)`
    Creates a flex div with it's components aligned to the right

- `def DivVStacked(*c, **kwargs)`
    Creates a flex div with it's components stacked vertically

- `def DivHStacked(*c, **kwargs)`
    Creates a flex div with it's components stacked horizontally

- `class NavT(Enum)`
    Members: default, primary, secondary


- `def NavContainer(*li, **kwargs)`
    Creates a navigation container (useful for creating a sidebar navigation).  A Nav is a list (NavBar is something different)

- `def NavParentLi(*nav_container, **kwargs)`
    Creates a navigation list item with a parent nav for nesting

- `def NavDividerLi(*c, **kwargs)`
    Creates a navigation list item with a divider

- `def NavHeaderLi(*c, **kwargs)`
    Creates a navigation list item with a header

- `def NavSubtitle(*c, **kwargs)`
    Creates a navigation subtitle

- `def NavCloseLi(*c, **kwargs)`
    Creates a navigation list item with a close button

- `class ScrollspyT(Enum)`
    Members: underline, bold


- `def NavBar(*c)`
    Creates a responsive navigation bar with mobile menu support

- `def SliderContainer(*c, **kwargs)`
    Creates a slider container

- `def SliderItems(*c, **kwargs)`
    Creates a slider items container

- `def SliderNav(cls, prev_cls, next_cls, **kwargs)`
    Navigation arrows for Slider component

- `def Slider(*c, **kwargs)`
    Creates a slider with optional navigation arrows

- `def DropDownNavContainer(*li, **kwargs)`
    A Nav that is part of a DropDown

- `def TabContainer(*li, **kwargs)`
    A TabContainer where children will be different tabs

- `class CardT(Enum)`
    Card styles from UIkit
    Members: default, primary, secondary, destructive, hover


- `def CardTitle(*c, **kwargs)`
    Creates a card title

- `def CardHeader(*c, **kwargs)`
    Creates a card header

- `def CardBody(*c, **kwargs)`
    Creates a card body

- `def CardFooter(*c, **kwargs)`
    Creates a card footer

- `def CardContainer(*c, **kwargs)`
    Creates a card container

- `def Card(*c, **kwargs)`
    Creates a Card with a header, body, and footer

- `class TableT(Enum)`
    Members: divider, striped, hover, sm, lg, justify, middle, responsive


- `def Table(*c, **kwargs)`
    Creates a table

- `def TableFromLists(header_data, body_data, footer_data, header_cell_render, body_cell_render, footer_cell_render, cls, sortable, **kwargs)`
    Creates a Table from a list of header data and a list of lists of body data

- `def TableFromDicts(header_data, body_data, footer_data, header_cell_render, body_cell_render, footer_cell_render, cls, sortable, **kwargs)`
    Creates a Table from a list of header data and a list of dicts of body data

- `def apply_classes(html_str, class_map, class_map_mods)`
    Apply classes to html string

- `def render_md(md_content, class_map, class_map_mods)`
    Renders markdown using mistletoe and lxml

- `def get_franken_renderer(img_dir)`
    Create a renderer class with the specified img_dir

- `def ThemePicker(color, radii, shadows, font, mode, cls, custom_themes)`
    Theme picker component with configurable sections

- `def LightboxContainer(*lightboxitem, **kwargs)`
    Lightbox container that will hold `LightboxItems`

- `def LightboxItem(*c, **kwargs)`
    Anchor tag with appropriate structure to go inside a `LightBoxContainer`

- `def ApexChart(**kws)`
    Apex chart component
</doc></api><examples><doc title="Websockets application" desc="Very brief example of using websockets with HTMX and FastHTML">from asyncio import sleep
from fasthtml.common import *

app = FastHTML(exts='ws')
rt = app.route

def mk_inp(): return Input(id='msg')
nid = 'notifications'

@rt('/')
async def get():
    cts = Div(
        Div(id=nid),
        Form(mk_inp(), id='form', ws_send=True),
        hx_ext='ws', ws_connect='/ws')
    return Titled('Websocket Test', cts)

async def on_connect(send): await send(Div('Hello, you have connected', id=nid))
async def on_disconnect( ): print('Disconnected!')

@app.ws('/ws', conn=on_connect, disconn=on_disconnect)
async def ws(msg:str, send):
    await send(Div('Hello ' + msg, id=nid))
    await sleep(2)
    return Div('Goodbye ' + msg, id=nid), mk_inp()

serve()
</doc><doc title="Todo list application" desc="Detailed walk-thru of a complete CRUD app in FastHTML showing idiomatic use of FastHTML and HTMX patterns.">### Walkthrough of an idiomatic fasthtml app ###

# This fasthtml app includes functionality from fastcore, starlette, fastlite, and fasthtml itself.
# Run with: `python adv_app.py`
# Importing from `fasthtml.common` brings the key parts of all of these together. We recommend using a wildcard import since only necessary parts are exported by the module.
from fasthtml.common import *
from hmac import compare_digest

# We recommend using sqlite for most apps, as it is simple, fast, and scalable. `database()` creates the db if it doesn't exist.
db = database('data/utodos.db')
# Create regular classes for your database tables. There are auto-converted to fastcore flexiclasses, which are like dataclasses, but with some extra functionality.
class User: name:str; pwd:str
class Todo: id:int; title:str; done:bool; name:str; details:str; priority:int
# The `create` method creates a table in the database, if it doesn't already exist. The `pk` argument specifies the primary key for the table. If not provided, it defaults to 'id'.
users = db.create(User, pk='name')
# The `transform` argument is used to automatically update the database table, if it exists, to match the class definition. It is a simple and effective migration system for less complex needs. Use the `fastmigrate` package for more sophisticated migrations.
todos = db.create(Todo, transform=True)

# Any Starlette response class can be returned by a FastHTML route handler. In that case, FastHTML won't change it at all.
login_redir = RedirectResponse('/login', status_code=303)

# The `before` function is a *Beforeware* function. These are functions that run before a route handler is called.
def before(req, sess):
    # This sets the `auth` attribute in the request scope, and gets it from the session. The session is a Starlette session, which is a dict-like object which is cryptographically signed, so it can't be tampered with.
    # The `auth` key in the scope is automatically provided to any handler which requests it, and can not be injected by the user using query params, cookies, etc, so it should be secure to use.
    auth = req.scope['auth'] = sess.get('auth', None)
    if not auth: return login_redir
    # `xtra` adds a filter to queries and DDL statements, to ensure that the user can only see/edit their own todos.
    todos.xtra(name=auth)

# Beforeware objects require the function itself, and optionally a list of regexes to skip.
bware = Beforeware(before, skip=[r'/favicon\.ico', r'/static/.*', r'.*\.css', '/login', '/send_login'])

markdown_js = """
import { marked } from "https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js";
proc_htmx('.markdown', e => e.innerHTML = marked.parse(e.textContent));
"""

# The `FastHTML` class is a subclass of `Starlette`, so you can use any parameters that `Starlette` accepts. In addition, you can add your Beforeware here, and any headers you want included in HTML responses.
def _not_found(req, exc): return Titled('Oh no!', Div('We could not find that page :('))
app = FastHTML(before=bware,
               # These are the same as Starlette exception_handlers, except they also support `FT` results
               exception_handlers={404: _not_found},
               # PicoCSS is a simple CSS system for getting started; for more complex styling try MonsterUI (which wraps uikit and Tailwind)
               hdrs=(picolink, # PicoCSS headers
                     # Look at fasthtml/js.py to see how to add Javascript libraries to FastHTML, like this one.
                     SortableJS('.sortable'),
                     # MarkdownJS is actually provided as part of FastHTML, but we've included the js code here so that you can see how it works.
                     Script(markdown_js, type='module'))
                )
# We add `rt` as a shortcut for `app.route`, which is what we'll use to decorate our route handlers.
rt = app.route

# FastHTML uses Starlette's path syntax, and adds a `static` type which matches standard static file extensions. You can define your own regex path specifiers -- for instance this is how `static` is defined in FastHTML `reg_re_param("static", "ico|gif|jpg|jpeg|webm|css|js|woff|png|svg|mp4|webp|ttf|otf|eot|woff2|txt|xml|html")`
# Provide param to `rt` to use full Starlette route syntax.
@rt("/{fname:path}.{ext:static}", methods=['GET'])
def static_handler(fname:str, ext:str): return FileResponse(f'{fname}.{ext}')

# This function handles GET and POST requests to the `/login` path, because the name of the function automatically becomes the path for the route handler, and GET/POST are available by default. We recommend generally sticking to just these two HTTP verbs.
@rt
def login():
    # This creates a form with two input fields, and a submit button. `Input`, `Form`, etc are `FT` (fasttag) objects. FastHTML composes them from trees and auto-converts them to HTML when needed.
    # You can also use plain HTML strings in handlers and headers, which will be auto-escaped, unless you use `Safe(...string...)`. If you want other custom tags (e.g. `MyTag`), they can be auto-generated by e.g:
    #   `from fasthtml.components import MyTag`.
    # fasttag objects are callable. Calling them adds children and attributes to the tag. Therefore you can use them like this:
    frm = Form(action=send_login, method='post')(
        # Tags with a `name` attr will have `name` auto-set to the same as `id` if not provided
        Input(id='name', placeholder='Name'),
        Input(id='pwd', type='password', placeholder='Password'),
        Button('login'))
    # If a user visits the URL directly, FastHTML auto-generates a full HTML page. However, if the URL is accessed by HTMX, then one HTML partial is created for each element of the tuple.
    # To avoid this auto-generation of a full page, return a `HTML` object, or a Starlette `Response`.
    # `Titled` returns a tuple of a `Title` with the first arg and a `Container` with the rest.
    # A handler can return either a single `FT` object or string, or a tuple of them.
    # In the case of a tuple, the stringified objects are concatenated and returned to the browser. The `Title` tag has a special purpose: it sets the title of the page (this is HTMX's built in behavior for title HTML partials).
    return Titled("Login", frm)

# Handlers are passed whatever information they "request" in the URL, as keyword arguments.
# This handler is called when a POST request is made to the `/login` path. The `login` argument is an instance of the `Login` class, which has been auto-instantiated from the form data.
# There are a number of special parameter names, which will be passed useful information about the request: `session`: the Starlette session; `request`: the Starlette request; `auth`: the value of `scope['auth']`, `htmx`: the HTMX headers, if any; `app`: the FastHTML app object.
# You can also pass any string prefix of `request` or `session`.
@rt
def send_login(name:str, pwd:str, sess):
    if not name or not pwd: return login_redir
    # Indexing into a table queries by primary key, which is `name` here.
    try: u = users[name]
    # If the primary key does not exist, the method raises a `NotFoundError`. Here we use this to just generate a user -- in practice you'd probably to redirect to a signup page.
    # Note that `insert` (and all similar db methods) returns the row object, so we can use it to get the new user.
    except NotFoundError: u = users.insert(name=name, pwd=pwd)
    if not compare_digest(u.pwd.encode("utf-8"), pwd.encode("utf-8")): return login_redir
    # Because the session is signed, we can securely add information to it. It's stored in the browser cookies. If you don't pass a secret signing key to `FastHTML`, it will auto-generate one and store it in a file `./sesskey`.
    sess['auth'] = u.name
    return RedirectResponse('/', status_code=303)

@rt
def logout(sess):
    del sess['auth']
    return login_redir

# Refactoring components in FastHTML is as simple as creating Python functions. The `clr_details` function creates a Div with specific HTMX attributes.
# `hx_swap_oob='innerHTML'` tells HTMX to swap the inner HTML of the target element out-of-band, meaning it will update this element regardless of where the HTMX request originated from. This returned div is empty, so it will clear the details view.
def clr_details(): return Div(hx_swap_oob='innerHTML', id='current-todo')

# Dataclasses, dicts, namedtuples, TypedDicts, and custom classes are automatically instantiated from form data.
# In this case, the `Todo` class is a flexiblass (a subclass of dataclass), so the handler will be passed all the field names of it.
@rt
def update(todo: Todo):
    # The updated todo is returned. By returning the updated todo, we can update the list directly. Because we return a tuple with `clr_details()`, the details view is also cleared.
    # Later on, the `__ft__` method of the `Todo` class will be called to convert it to a fasttag.
    return todos.update(todo), clr_details()

@rt
def edit(id:int):
    # `target_id` specifies which element will be updated with the server's response (it's a shortcut for hx_target=f"#{...}").
    # CheckboxX add an extra hidden field with the same name as the checkbox, so that it can be submitted as part of the form data. This is useful for boolean fields, where you want to know if the field was checked or not.
    res = Form(hx_post=update, target_id=f'todo-{id}', id="edit")(
        Group(Input(id="title"), Button("Save")),
        Hidden(id="id"), CheckboxX(id="done", label='Done'),
        Textarea(id="details", name="details", rows=10))
    # `fill_form` populates the form with existing todo data, and returns the result. Indexing into a table (`todos`) queries by primary key, which is `id` here. It also includes `xtra`, so this will only return the id if it belongs to the current user.
    return fill_form(res, todos[id])

@rt
def rm(id:int):
    # `delete` removes the item with the given primary key.
    todos.delete(id)
    # Returning `clr_details()` ensures the details view is cleared after deletion, leveraging HTMX's out-of-band swap feature.
    # Note that we are not returning *any* FT component that doesn't have an "OOB" swap, so the target element inner HTML is simply deleted.
    return clr_details()

@rt
def show(id:int):
    todo = todos[id]
    # `hx_swap` determines how the update should occur. We use "outerHTML" to replace the entire todo `Li` element.
    # `rm.to(id=todo.id)` is a shortcut for `f'/rm?id={todo.id}'`. All routes have this `to` method.
    btn = Button('delete', hx_post=rm.to(id=todo.id),
                 hx_target=f'#todo-{todo.id}', hx_swap="outerHTML")
    # The "markdown" class is used here because that's the CSS selector we used in the JS earlier. This will trigger the JS to parse the markdown.
    # Because `class` is a reserved keyword in Python, we use `cls` instead, which FastHTML auto-converts.
    return Div(H2(todo.title), Div(todo.details, cls="markdown"), btn)

# `fastcore.patch` adds a method to an existing class.
# The `__ft__` method is a special method that FastHTML uses to convert the object into an `FT` object, so that it can be composed into an FT tree, and later rendered into HTML.
@patch
def __ft__(self:Todo):
    # Some FastHTML tags have an 'X' suffix, which means they're "extended" in some way. For instance, here `AX` is an extended `A` tag, which takes 3 positional arguments: `(text, hx_get, target_id)`.
    # All underscores in FT attrs are replaced with hyphens, so this will create an `hx-get` attr, which HTMX uses to trigger a GET request.
    # Generally, most of your route handlers in practice (as in this demo app) are likely to be HTMX handlers.
    ashow = AX(self.title, show.to(id=self.id), 'current-todo')
    aedit = AX('edit',     edit.to(id=self.id), 'current-todo')
    dt = '✅ ' if self.done else ''
    # FastHTML provides some shortcuts. For instance, `Hidden` is defined as simply: `return Input(type="hidden", value=value, **kwargs)`
    cts = (dt, ashow, ' | ', aedit, Hidden(id="id", value=self.id), Hidden(id="priority", value="0"))
    # Any FT object can take a list of children as positional args, and a dict of attrs as keyword args.
    return Li(*cts, id=f'todo-{self.id}')

@rt
def create(todo:Todo):
    # `hx_swap_oob='true'` tells HTMX to perform an out-of-band swap, updating this element wherever it appears. This is used to clear the input field after adding the new todo.
    new_inp =  Input(id="new-title", name="title", placeholder="New Todo", hx_swap_oob='true')
    # `insert` returns the inserted todo, which is appended to the list start, because we used `hx_swap='afterbegin'` when creating the form.
    return todos.insert(todo), new_inp

# Because the todo list form created earlier included hidden inputs with the todo IDs, they are included in the form data. By using a parameter called (e.g) "id", FastHTML will try to find something suitable in the request with this name. In order, it searches as follows: path; query; cookies; headers; session keys; form data.
# FastHTML will use your parameter's type annotation to try to cast the value to the requested type. In the case of form data, there can be multiple values with the same key. So in this case, the parameter is a list of ints.
@rt
def reorder(id:list[int]):
    # Normally running a query in a loop like this would be really slow. But sqlite is at least as fast as a file system, so this pattern is actually idiomatic and efficient.
    for i,id_ in enumerate(id): todos.update({'priority':i}, id_)
    # HTMX by default replaces the inner HTML of the calling element, which in this case is the todo list form. Therefore, we return the list of todos, now in the correct order, which will be auto-converted to FT for us.
    # In this case, it's not strictly necessary, because sortable.js has already reorder the DOM elements. However, by returning the updated data, we can be assured that there aren't sync issues between the DOM and the server.
    return tuple(todos(order_by='priority'))

# This is the handler for the main todo list application. By including the `auth` parameter, it gets passed the current username, for displaying in the title. `index()` is a special name for the main route handler, and is called when the root path `/` is accessed.
@rt
def index(auth):
    title = f"{auth}'s Todo list"
    top = Grid(H1(title), Div(A('logout', href=logout), style='text-align: right'))
    new_inp = Input(id="new-title", name="title", placeholder="New Todo")
    add = Form(Group(new_inp, Button("Add")),
               hx_post=create, target_id='todo-list', hx_swap="afterbegin")
    # Treating a table as a callable (i.e with `todos(...)` here) queries the table. Because we called `xtra` in our Beforeware, this queries the todos for the current user only.
    # We can include the todo objects directly as children of the `Form`, because the `Todo` class has `__ft__` defined. This is automatically called by FastHTML to convert the `Todo` objects into `FT` objects when needed.
    # The reason we put the todo list inside a form is so that we can use the 'sortable' js library to reorder them. That library calls the js `end` event when dragging is complete, so our trigger here causes our `/reorder` handler to be called.
    frm = Form(*todos(order_by='priority'),
               id='todo-list', cls='sortable', hx_post=reorder, hx_trigger="end")
    # We create an empty 'current-todo' Div at the bottom of our page, as a target for the details and editing views.
    card = Card(P('Drag/drop todos to reorder them'),
                Ul(frm),
                header=add, footer=Div(id='current-todo'))
    # PicoCSS uses `<Main class='container'>` page content; `Container` is a tiny function that generates that.
    return Title(title), Container(top, card)

# You do not need `if __name__ == '__main__':` in FastHTML apps, because `serve()` handles this automatically. By default it reloads the app when the source code changes.
serve()</doc></examples><optional><doc title="Surreal" desc="Tiny jQuery alternative for plain Javascript with inline Locality of Behavior, providing `me` and `any` functions"># 🗿 Surreal
### Tiny jQuery alternative for plain Javascript with inline [Locality of Behavior](https://htmx.org/essays/locality-of-behaviour/)!

![cover](https://user-images.githubusercontent.com/24665/171092805-b41286b2-be4a-4aab-9ee6-d604699cc507.png)
(Art by [shahabalizadeh](https://www.deviantart.com/shahabalizadeh))
<!--
<a href="https://github.com/gnat/surreal/archive/refs/heads/main.zip"><img src="https://img.shields.io/badge/Download%20.zip-ff9800?style=for-the-badge&color=%234400e5" alt="Download badge" /></a>

<a href="https://github.com/gnat/surreal"><img src="https://img.shields.io/github/workflow/status/gnat/surreal/ci?label=ci&style=for-the-badge&color=%237d91ce" alt="CI build badge" /></a>
<a href="https://github.com/gnat/surreal/releases"><img src="https://img.shields.io/github/workflow/status/gnat/surreal/release?label=Mini&style=for-the-badge&color=%237d91ce" alt="Mini build badge" /></a>
<a href="https://github.com/gnat/surreal/blob/main/LICENSE"><img src="https://img.shields.io/github/license/gnat/surreal?style=for-the-badge&color=%234400e5" alt="License badge" /></a>-->

## Why does this exist?

For devs who love ergonomics! You may appreciate Surreal if:

* You want to stay as close as possible to Vanilla JS.
* Hate typing `document.querySelector` over.. and over..
* Hate typing `addEventListener` over.. and over..
* Really wish `document.querySelectorAll` had Array functions..
* Really wish `this` would work in any inline `<script>` tag
* Enjoyed using jQuery selector syntax.
* [Animations, timelines, tweens](#-quick-start) with no extra libraries.
* Only 320 lines. No build step. No dependencies.
* Pairs well with [htmx](https://htmx.org)
* Want fewer layers, less complexity. Are aware of the cargo cult. ✈️

## ✨ What does it add to Javascript?

* ⚡️ [Locality of Behavior (LoB)](https://htmx.org/essays/locality-of-behaviour/) Use `me()` inside `<script>`
  * No **.class** or **#id** needed! Get an element without creating a unique name.
  * `this` but much more flexible!
  * Want `me` in your CSS `<style>` tags, too? See our [companion script](https://github.com/gnat/css-scope-inline)
* 🔗 Call chaining, jQuery style.
* ♻️ Functions work seamlessly on 1 element or arrays of elements!
  * All functions can use: `me()`, `any()`, `NodeList`, `HTMLElement` (..or arrays of these!)
  * Get 1 element: `me()`
  * ..or many elements: `any()`
  * `me()` or `any()` can chain with any Surreal function.
    * `me()` can be used directly as a single element (like `querySelector()` or `$()`)
    * `any()` can use: `for` / `forEach` / `filter` / `map` (like `querySelectorAll()` or `$()`)
* 🌗 No forced style. Use: `classAdd` or `class_add` or `addClass` or `add_class`
  * Use `camelCase` (Javascript) or `snake_case` (Python, Rust, PHP, Ruby, SQL, CSS).

### 🤔 Why use `me()` / `any()` instead of `$()`
* 💡 Solves the classic jQuery bloat problem: Am I getting 1 element or an array of elements?
  * `me()` is guaranteed to return 1 element (or first found, or null).
  * `any()` is guaranteed to return an array (or empty array).
  * No more checks = write less code. Bonus: Reads more like self-documenting english.

## 👁️ How does it look?

Do surreal things with [Locality of Behavior](https://htmx.org/essays/locality-of-behaviour/) like:
```html
<label for="file-input" >
  <div class="uploader"></div>
  <script>
    me().on("dragover", ev => { halt(ev); me(ev).classAdd('.hover'); console.log("Files in drop zone.") })
    me().on("dragleave", ev => { halt(ev); me(ev).classRemove('.hover'); console.log("Files left drop zone.") })
    me().on("drop", ev => { halt(ev); me(ev).classRemove('.hover').classAdd('.loading'); me('#file-input').attribute('files', ev.dataTransfer.files); me('#form').send('change') })
  </script>
</label>
```

See the [Live Example](https://gnat.github.io/surreal/example.html)! Then [view source](https://github.com/gnat/surreal/blob/main/example.html).

## 🎁 Install

Surreal is only 320 lines. No build step. No dependencies.

[📥 Download](https://raw.githubusercontent.com/gnat/surreal/main/surreal.js) into your project, and add `<script src="/surreal.js"></script>` in your `<head>`

Or, 🌐 via CDN: `<script src="https://cdnjs.cloudflare.com/ajax/libs/surreal/1.3.2/surreal.js"></script>`

## ⚡ Usage

### <a name="selectors"></a>🔍️ DOM Selection

* Select **one** element: `me(...)`
  * Can be any of:
    * CSS selector: `".button"`, `"#header"`, `"h1"`, `"body > .block"`
    * Variables: `body`, `e`, `some_element`
    * Events: `event.currentTarget` will be used.
    * Surreal selectors: `me()`,`any()`
    * Choose the start location in the DOM with the 2nd arg. (Default: `document`)
      * 🔥 `any('button', me('#header')).classAdd('red')`
        * Add `.red` to any `<button>` inside of `#header`
  * `me()` ⭐ Get parent element of `<script>` without a **.class** or **#id** !
  * `me("body")` Gets `<body>`
  * `me(".button")` Gets the first `<div class="button">...</div>`. To get all of them use `any()`
* Select **one or more** elements as an array: `any(...)`
  * Like `me()` but guaranteed to return an array (or empty array). 
  * `any(".foo")` ⭐ Get all matching elements.
  * Convert between arrays of elements and single elements: `any(me())`, `me(any(".something"))`
 
### 🔥 DOM Functions

* ♻️ All functions work on single elements or arrays of elements.
* 🔗 Start a chain using `me()` and `any()`
  * 🟢 Style A `me().classAdd('red')` ⭐ Chain style. Recommended!
  * 🟠 Style B: `classAdd(me(), 'red')`
* 🌐 Global conveniences help you write less code.
  * `globalsAdd()` will automatically warn you of any clobbering issues!
  * 💀🩸 If you want no conveniences, or are a masochist, delete `globalsAdd()`
    * 🟢 `me().classAdd('red')` becomes `surreal.me().classAdd('red')`
    * 🟠 `classAdd(me(), 'red')` becomes `surreal.classAdd(surreal.me(), 'red')`

See: [Quick Start](#quick-start) and [Reference](#reference) and [No Surreal Needed](#no-surreal)

## <a name="quick-start"></a>⚡ Quick Start

* Add a class
  * `me().classAdd('red')`
  * `any("button").classAdd('red')`
* Events
  * `me().on("click", ev => me(ev).fadeOut() )`
  * `any('button').on('click', ev => { me(ev).styles('color: red') })`
* Run functions over elements.
  * `any('button').run(_ => { alert(_) })`
* Styles / CSS
  * `me().styles('color: red')`
  * `me().styles({ 'color':'red', 'background':'blue' })`
* Attributes
  * `me().attribute('active', true)`

<a name="timelines"></a>
#### Timeline animations without any libraries.
```html
<div>I change color every second.
  <script>
    // On click, animate something new every second.
    me().on("click", async ev => {
      let el = me(ev) // Save target because async will lose it.
      me(el).styles({ "transition": "background 1s" })
      await sleep(1000)
      me(el).styles({ "background": "red" })
      await sleep(1000)
      me(el).styles({ "background": "green" })
      await sleep(1000)
      me(el).styles({ "background": "blue" })
      await sleep(1000)
      me(el).styles({ "background": "none" })
      await sleep(1000)
      me(el).remove()
    })
  </script>
</div>
```
```html
<div>I fade out and remove myself.
  <script>me().on("click", ev => { me(ev).fadeOut() })</script>
</div>
```
```html
<div>Change color every second.
  <script>
    // Run immediately.
    (async (e = me()) => {
      me(e).styles({ "transition": "background 1s" })
      await sleep(1000)
      me(e).styles({ "background": "red" })
      await sleep(1000)
      me(e).styles({ "background": "green" })
      await sleep(1000)
      me(e).styles({ "background": "blue" })
      await sleep(1000)
      me(e).styles({ "background": "none" })
      await sleep(1000)
      me(e).remove()
    })()
  </script>
</div>
```
```html
<script>
  // Run immediately, for every <button> globally!
  (async () => {
    any("button").fadeOut()
  })()
</script>
```
#### Array methods
```js
any('button')?.forEach(...)
any('button')?.map(...)
```

## <a name="reference"></a>👁️ Functions
Looking for [DOM Selectors](#selectors)?
Looking for stuff [we recommend doing in vanilla JS](#no-surreal)?
### 🧭 Legend
* 🔗 Chainable off `me()` and `any()`
* 🌐 Global shortcut.
* 🔥 Runnable example.
* 🔌 Built-in Plugin
### 👁️ At a glance

* 🔗 `run`
  * It's `forEach` but less wordy and works on single elements, too!
  * 🔥 `me().run(e => { alert(e) })`
  * 🔥 `any('button').run(e => { alert(e) })`
* 🔗 `remove`
  * 🔥 `me().remove()`
  * 🔥 `any('button').remove()`
* 🔗 `classAdd` 🌗 `class_add` 🌗 `addClass` 🌗 `add_class`
  * 🔥 `me().classAdd('active')`
  * Leading `.` is **optional**
    * Same thing: `me().classAdd('active')` 🌗 `me().classAdd('.active')`
* 🔗 `classRemove` 🌗 `class_remove` 🌗 `removeClass` 🌗 `remove_class`
  * 🔥 `me().classRemove('active')`
* 🔗 `classToggle` 🌗 `class_toggle` 🌗 `toggleClass` 🌗 `toggle_class`
  * 🔥 `me().classToggle('active')`
* 🔗 `styles`
  * 🔥 `me().styles('color: red')` Add style.
  * 🔥 `me().styles({ 'color':'red', 'background':'blue' })` Add multiple styles.
  * 🔥 `me().styles({ 'background':null })` Remove style.
* 🔗 `attribute` 🌗 `attributes` 🌗 `attr`
  * Get: 🔥 `me().attribute('data-x')`
    * For single elements.
    * For many elements, wrap it in: `any(...).run(...)` or `any(...).forEach(...)`
  * Set: 🔥`me().attribute('data-x', true)`
  * Set multiple: 🔥 `me().attribute({ 'data-x':'yes', 'data-y':'no' })`
  * Remove: 🔥 `me().attribute('data-x', null)`
  * Remove multiple: 🔥 `me().attribute({ 'data-x': null, 'data-y':null })`
* 🔗 `send` 🌗 `trigger`
  * 🔥 `me().send('change')`
  * 🔥 `me().send('change', {'data':'thing'})`
  * Wraps `dispatchEvent`
* 🔗 `on`
  * 🔥 `me().on('click', ev => { me(ev).styles('background', 'red') })`
  * Wraps `addEventListener`
* 🔗 `off`
  * 🔥 `me().off('click', fn)`
  * Wraps `removeEventListener`
* 🔗 `offAll`
  * 🔥 `me().offAll()`
* 🔗 `disable`
  * 🔥 `me().disable()`
  * Easy alternative to `off()`. Disables click, key, submit events.
* 🔗 `enable`
  * 🔥 `me().enable()`
  * Opposite of `disable()`
* 🌐 `createElement` 🌗 `create_element`
  * 🔥 `e_new = createElement("div"); me().prepend(e_new)`
  * Alias of [document.createElement](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement)
* 🌐 `sleep`
  * 🔥 `await sleep(1000, ev => { alert(ev) })`
  * `async` version of `setTimeout`
  * Wonderful for animation timelines.
* 🌐 `halt`
  * 🔥 `halt(event)`
  * When recieving an event, stop propagation, and prevent default actions (such as form submit).
  * Wrapper for [stopPropagation](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation) and [preventDefault](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
* 🌐 `tick`
  * 🔥 `await tick()`
  * `await` version of `rAF` / `requestAnimationFrame`.
  * Waits for 1 frame (browser paint).
  * Useful to guarantee CSS properties are applied, and events have propagated.
* 🌐 `rAF`
  * 🔥 `rAF(e => { return e })`
  * Calls after 1 frame (browser paint). Alias of [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)
  * Useful to guarantee CSS properties are applied, and events have propagated.
* 🌐 `rIC`
  * 🔥 `rIC(e => { return e })`
  * Calls when Javascript is idle. Alias of [requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback)
* 🌐 `onloadAdd` 🌗 `onload_add` 🌗 `addOnload` 🌗 `add_onload`
  * 🔥 `onloadAdd(_ => { alert("loaded!"); })`
  * 🔥 `<script>let e = me(); onloadAdd(_ => { me(e).on("click", ev => { alert("clicked") }) })</script>`
  * Execute after the DOM is ready. Similar to jquery `ready()`
  * Add to `window.onload` while preventing overwrites of `window.onload` and predictable loading!
  * Alternatives:
    * Skip missing elements using `?.` example: `me("video")?.requestFullscreen()`
    * Place `<script>` after the loaded element.
      * See `me('-')` / `me('prev')`
* 🔌 `fadeOut`
  * See below
* 🔌 `fadeIn`
  * See below

### <a name="plugin-included"></a>🔌 Built-in Plugins

### Effects
Build effects with `me().styles({...})` with timelines using [CSS transitioned `await` or callbacks](#timelines).

Common effects included:

* 🔗 `fadeOut` 🌗 `fade_out`
  * Fade out and remove element.
  * Keep element with `remove=false`.
  * 🔥 `me().fadeOut()`
  * 🔥 `me().fadeOut(ev => { alert("Faded out!") }, 3000)` Over 3 seconds then call function.

* 🔗 `fadeIn` 🌗 `fade_in`
  * Fade in existing element which has `opacity: 0`
  * 🔥 `me().fadeIn()`
  * 🔥 `me().fadeIn(ev => { alert("Faded in!") }, 3000)` Over 3 seconds then call function.


## <a name="no-surreal"></a>⚪ No Surreal Needed

More often than not, Vanilla JS is the easiest way!

Logging
* 🔥 `console.log()` `console.warn()` `console.error()`
* Event logging: 🔥 `monitorEvents(me())` See: [Chrome Blog](https://developer.chrome.com/blog/quickly-monitor-events-from-the-console-panel-2/)

Benchmarking / Time It!
* 🔥 `console.time('name')`
* 🔥 `console.timeEnd('name')`

Text / HTML Content
* 🔥 `me().textContent = "hello world"`
  * XSS Safe! See: [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent)
* 🔥 `me().innerHTML = "<p>hello world</p>"`
* 🔥 `me().innerText = "hello world"`

Children
* 🔥 `me().children`
* 🔥 `me().children.hidden = true`

Append / Prepend elements.
* 🔥 `me().prepend(new_element)`
* 🔥 `me().appendChild(new_element)`
* 🔥 `me().insertBefore(element, other_element.firstChild)`
* 🔥 `me().insertAdjacentHTML("beforebegin", new_element)`

AJAX (replace jQuery `ajax()`)
* Use [htmx](https://htmx.org/) or [htmz](https://leanrada.com/htmz/) or [fetch()](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) or [XMLHttpRequest()](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
* Example using `fetch()`
```js
me().on("click", async event => {
  let e = me(event)
  // EXAMPLE 1: Hit an endpoint.
  if((await fetch("/webhook")).ok) console.log("Did the thing.")
  // EXAMPLE 2: Get content and replace me()
  try {
    let response = await fetch('/endpoint')
    if (response.ok) e.innerHTML = await response.text()
    else console.warn('fetch(): Bad response')
  }
  catch (error) { console.warn(`fetch(): ${error}`) }
})
```
* Example using `XMLHttpRequest()`
```js
me().on("click", async event => {
  let e = me(event)
  // EXAMPLE 1: Hit an endpoint.
  var xhr = new XMLHttpRequest()
  xhr.open("GET", "/webhook")
  xhr.send()
  // EXAMPLE 2: Get content and replace me()
  var xhr = new XMLHttpRequest()
  xhr.open("GET", "/endpoint")
  xhr.onreadystatechange = () => {
    if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status < 300) e.innerHTML = xhr.responseText
  }
  xhr.send()
})
```

 ## 💎 Conventions & Tips

* Many ideas can be done in HTML / CSS (ex: dropdowns)
* `_` = for temporary or unused variables. Keep it short and sweet!
* `e`, `el`, `elt` = element
* `e`, `ev`, `evt` = event
* `f`, `fn` = function

#### Scope functions and variables inside `<script>`
  * ⭐ Use a block `{ let note = "hi"; function hey(text) { alert(text) }; me().on('click', ev => { hey(note) }) }`
    * `let` and `function` is scoped within `{ }`
  * ⭐ Use `me()`
    *  `me().hey = (text) => { alert(text) }`
    *  `me().on('click', (ev) => { me(ev).hey("hi") })`
  * ⭐ Use an event `me().on('click', ev => { /* add and call function here */ })`
  * Use an inline module: `<script type="module">`
    * Note: `me()` in modules will not see `parentElement`, explicit selectors are required: `me(".mybutton")`

#### Select a void element like `<input type="text" />`
* Use: `me('-')` or `me('prev')` or `me('previous')`
  * 🔥 `<input type="text" /> <script>me('-').value = "hello"</script>`
  * Inspired by the CSS "next sibling" combinator `+` but in reverse `-`
* Or, use a relative start.
  * 🔥 `<form> <input type="text" n1 /> <script>me('[n1]', me()).value = "hello"</script> </form>`

#### Ignore call chain when element is missing.
* 🔥 `me("#i_dont_exist")?.classAdd('active')`
* No warnings: 🔥 `me("#i_dont_exist", document, false)?.classAdd('active')`

## <a name="plugins"></a>🔌 Your own plugin

Feel free to edit Surreal directly- but if you prefer, you can use plugins to effortlessly merge with new versions.

```javascript
function pluginHello(e) {
  function hello(e, name="World") {
    console.log(`Hello ${name} from ${e}`)
    return e // Make chainable.
  }
  // Add sugar
  e.hello = (name) => { return hello(e, name) }
}

surreal.plugins.push(pluginHello)
```

Now use your function like: `me().hello("Internet")`

* See the included `pluginEffects` for a more comprehensive example.
* Your functions are added globally by `globalsAdd()` If you do not want this, add it to the `restricted` list.
* Refer to an existing function to see how to make yours work with 1 or many elements.

Make an [issue](https://github.com/gnat/surreal/issues) or [pull request](https://github.com/gnat/surreal/pulls) if you think people would like to use it! If it's useful enough we'll want it in core.

### ⭐ Awesome Surreal examples, plugins, and resources: [awesome-surreal](https://github.com/gnat/awesome-surreal) !

## 📚️ Inspired by

* [jQuery](https://jquery.com/) for the chainable syntax we all love.
* [BlingBling.js](https://github.com/argyleink/blingblingjs) for modern minimalism.
* [Bliss.js](https://blissfuljs.com/) for a focus on single elements and extensibility.
* [Hyperscript](https://hyperscript.org) for Locality of Behavior and awesome ergonomics.
* Shout out to [Umbrella](https://umbrellajs.com/), [Cash](https://github.com/fabiospampinato/cash), [Zepto](https://zeptojs.com/)- Not quite as ergonomic. Requires build step to extend.

## 🌘 Future
* Always more `example.html` goodies!
* Automated browser testing perhaps with:
  * [Fava](https://github.com/fabiospampinato/fava). See: https://github.com/avajs/ava/issues/24#issuecomment-885949036
  * [Ava](https://github.com/avajs/ava/blob/main/docs/recipes/browser-testing.md)
  * [jsdom](https://github.com/jsdom/jsdom)
    * [jsdom notes](https://github.com/jsdom/jsdom#executing-scripts)</doc><doc title="Starlette full documentation" desc="A subset of the Starlette documentation useful for FastHTML development."># index.md

---

# Starlette Introduction

Starlette is a lightweight [ASGI][asgi] framework/toolkit,
which is ideal for building async web services in Python.

It is production-ready, and gives you the following:

* A lightweight, low-complexity HTTP web framework.
* WebSocket support.
* In-process background tasks.
* Startup and shutdown events.
* Test client built on `httpx`.
* CORS, GZip, Static Files, Streaming responses.
* Session and Cookie support.
* 100% test coverage.
* 100% type annotated codebase.
* Few hard dependencies.
* Compatible with `asyncio` and `trio` backends.
* Great overall performance [against independent benchmarks][techempower].

## Requirements

Python 3.8+

## Installation

```shell
$ pip3 install starlette
```

You'll also want to install an ASGI server, such as [uvicorn](http://www.uvicorn.org/), [daphne](https://github.com/django/daphne/), or [hypercorn](https://pgjones.gitlab.io/hypercorn/).

```shell
$ pip3 install uvicorn
```

## Example

**example.py**:

```python
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route


async def homepage(request):
    return JSONResponse({'hello': 'world'})


app = Starlette(debug=True, routes=[
    Route('/', homepage),
])
```

Then run the application...

```shell
$ uvicorn example:app
```

For a more complete example, [see here](https://github.com/encode/starlette-example).

## Dependencies

Starlette only requires `anyio`, and the following dependencies are optional:

* [`httpx`][httpx] - Required if you want to use the `TestClient`.
* [`jinja2`][jinja2] - Required if you want to use `Jinja2Templates`.
* [`python-multipart`][python-multipart] - Required if you want to support form parsing, with `request.form()`.
* [`itsdangerous`][itsdangerous] - Required for `SessionMiddleware` support.
* [`pyyaml`][pyyaml] - Required for `SchemaGenerator` support.

You can install all of these with `pip3 install starlette[full]`.

## Framework or Toolkit

Starlette is designed to be used either as a complete framework, or as
an ASGI toolkit. You can use any of its components independently.

```python
from starlette.responses import PlainTextResponse


async def app(scope, receive, send):
    assert scope['type'] == 'http'
    response = PlainTextResponse('Hello, world!')
    await response(scope, receive, send)
```

Run the `app` application in `example.py`:

```shell
$ uvicorn example:app
INFO: Started server process [11509]
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```

Run uvicorn with `--reload` to enable auto-reloading on code changes.

## Modularity

The modularity that Starlette is designed on promotes building re-usable
components that can be shared between any ASGI framework. This should enable
an ecosystem of shared middleware and mountable applications.

The clean API separation also means it's easier to understand each component
in isolation.

---

# applications.md

Starlette includes an application class `Starlette` that nicely ties together all of
its other functionality.

```python
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.routing import Route, Mount, WebSocketRoute
from starlette.staticfiles import StaticFiles


def homepage(request):
    return PlainTextResponse('Hello, world!')

def user_me(request):
    username = "John Doe"
    return PlainTextResponse('Hello, %s!' % username)

def user(request):
    username = request.path_params['username']
    return PlainTextResponse('Hello, %s!' % username)

async def websocket_endpoint(websocket):
    await websocket.accept()
    await websocket.send_text('Hello, websocket!')
    await websocket.close()

def startup():
    print('Ready to go')


routes = [
    Route('/', homepage),
    Route('/user/me', user_me),
    Route('/user/{username}', user),
    WebSocketRoute('/ws', websocket_endpoint),
    Mount('/static', StaticFiles(directory="static")),
]

app = Starlette(debug=True, routes=routes, on_startup=[startup])
```

### Instantiating the application

::: starlette.applications.Starlette
    :docstring:

### Storing state on the app instance

You can store arbitrary extra state on the application instance, using the
generic `app.state` attribute.

For example:

```python
app.state.ADMIN_EMAIL = 'admin@example.org'
```

### Accessing the app instance

Where a `request` is available (i.e. endpoints and middleware), the app is available on `request.app`.


# requests.md


Starlette includes a `Request` class that gives you a nicer interface onto
the incoming request, rather than accessing the ASGI scope and receive channel directly.

### Request

Signature: `Request(scope, receive=None)`

```python
from starlette.requests import Request
from starlette.responses import Response


async def app(scope, receive, send):
    assert scope['type'] == 'http'
    request = Request(scope, receive)
    content = '%s %s' % (request.method, request.url.path)
    response = Response(content, media_type='text/plain')
    await response(scope, receive, send)
```

Requests present a mapping interface, so you can use them in the same
way as a `scope`.

For instance: `request['path']` will return the ASGI path.

If you don't need to access the request body you can instantiate a request
without providing an argument to `receive`.

#### Method

The request method is accessed as `request.method`.

#### URL

The request URL is accessed as `request.url`.

The property is a string-like object that exposes all the
components that can be parsed out of the URL.

For example: `request.url.path`, `request.url.port`, `request.url.scheme`.

#### Headers

Headers are exposed as an immutable, case-insensitive, multi-dict.

For example: `request.headers['content-type']`

#### Query Parameters

Query parameters are exposed as an immutable multi-dict.

For example: `request.query_params['search']`

#### Path Parameters

Router path parameters are exposed as a dictionary interface.

For example: `request.path_params['username']`

#### Client Address

The client's remote address is exposed as a named two-tuple `request.client` (or `None`).

The hostname or IP address: `request.client.host`

The port number from which the client is connecting: `request.client.port`

#### Cookies

Cookies are exposed as a regular dictionary interface.

For example: `request.cookies.get('mycookie')`

Cookies are ignored in case of an invalid cookie. (RFC2109)

#### Body

There are a few different interfaces for returning the body of the request:

The request body as bytes: `await request.body()`

The request body, parsed as form data or multipart: `async with request.form() as form:`

The request body, parsed as JSON: `await request.json()`

You can also access the request body as a stream, using the `async for` syntax:

```python
from starlette.requests import Request
from starlette.responses import Response

    
async def app(scope, receive, send):
    assert scope['type'] == 'http'
    request = Request(scope, receive)
    body = b''
    async for chunk in request.stream():
        body += chunk
    response = Response(body, media_type='text/plain')
    await response(scope, receive, send)
```

If you access `.stream()` then the byte chunks are provided without storing
the entire body to memory. Any subsequent calls to `.body()`, `.form()`, or `.json()`
will raise an error.

In some cases such as long-polling, or streaming responses you might need to
determine if the client has dropped the connection. You can determine this
state with `disconnected = await request.is_disconnected()`.

#### Request Files

Request files are normally sent as multipart form data (`multipart/form-data`).

Signature: `request.form(max_files=1000, max_fields=1000)`

You can configure the number of maximum fields or files with the parameters `max_files` and `max_fields`:

```python
async with request.form(max_files=1000, max_fields=1000):
    ...
```

!!! info
    These limits are for security reasons, allowing an unlimited number of fields or files could lead to a denial of service attack by consuming a lot of CPU and memory parsing too many empty fields.

When you call `async with request.form() as form` you receive a `starlette.datastructures.FormData` which is an immutable
multidict, containing both file uploads and text input. File upload items are represented as instances of `starlette.datastructures.UploadFile`.

`UploadFile` has the following attributes:

* `filename`: An `str` with the original file name that was uploaded or `None` if its not available (e.g. `myimage.jpg`).
* `content_type`: An `str` with the content type (MIME type / media type) or `None` if it's not available (e.g. `image/jpeg`).
* `file`: A <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" target="_blank">`SpooledTemporaryFile`</a> (a <a href="https://docs.python.org/3/glossary.html#term-file-like-object" target="_blank">file-like</a> object). This is the actual Python file that you can pass directly to other functions or libraries that expect a "file-like" object.
* `headers`: A `Headers` object. Often this will only be the `Content-Type` header, but if additional headers were included in the multipart field they will be included here. Note that these headers have no relationship with the headers in `Request.headers`.
* `size`: An `int` with uploaded file's size in bytes. This value is calculated from request's contents, making it better choice to find uploaded file's size than `Content-Length` header. `None` if not set.

`UploadFile` has the following `async` methods. They all call the corresponding file methods underneath (using the internal `SpooledTemporaryFile`).

* `async write(data)`: Writes `data` (`bytes`) to the file.
* `async read(size)`: Reads `size` (`int`) bytes of the file.
* `async seek(offset)`: Goes to the byte position `offset` (`int`) in the file.
    * E.g., `await myfile.seek(0)` would go to the start of the file.
* `async close()`: Closes the file.

As all these methods are `async` methods, you need to "await" them.

For example, you can get the file name and the contents with:

```python
async with request.form() as form:
    filename = form["upload_file"].filename
    contents = await form["upload_file"].read()
```

!!! info
    As settled in [RFC-7578: 4.2](https://www.ietf.org/rfc/rfc7578.txt), form-data content part that contains file 
    assumed to have `name` and `filename` fields in `Content-Disposition` header: `Content-Disposition: form-data;
    name="user"; filename="somefile"`. Though `filename` field is optional according to RFC-7578, it helps 
    Starlette to differentiate which data should be treated as file. If `filename` field was supplied, `UploadFile` 
    object will be created to access underlying file, otherwise form-data part will be parsed and available as a raw 
    string.

#### Application

The originating Starlette application can be accessed via `request.app`.

#### Other state

If you want to store additional information on the request you can do so
using `request.state`.

For example:

`request.state.time_started = time.time()`


# responses.md


Starlette includes a few response classes that handle sending back the
appropriate ASGI messages on the `send` channel.

### Response

Signature: `Response(content, status_code=200, headers=None, media_type=None)`

* `content` - A string or bytestring.
* `status_code` - An integer HTTP status code.
* `headers` - A dictionary of strings.
* `media_type` - A string giving the media type. eg. "text/html"

Starlette will automatically include a Content-Length header. It will also
include a Content-Type header, based on the media_type and appending a charset
for text types, unless a charset has already been specified in the `media_type`.

Once you've instantiated a response, you can send it by calling it as an
ASGI application instance.

```python
from starlette.responses import Response


async def app(scope, receive, send):
    assert scope['type'] == 'http'
    response = Response('Hello, world!', media_type='text/plain')
    await response(scope, receive, send)
```
#### Set Cookie

Starlette provides a `set_cookie` method to allow you to set cookies on the response object.

Signature: `Response.set_cookie(key, value, max_age=None, expires=None, path="/", domain=None, secure=False, httponly=False, samesite="lax")`

* `key` - A string that will be the cookie's key.
* `value` - A string that will be the cookie's value.
* `max_age` - An integer that defines the lifetime of the cookie in seconds. A negative integer or a value of `0` will discard the cookie immediately. `Optional`
* `expires` - Either an integer that defines the number of seconds until the cookie expires, or a datetime. `Optional`
* `path` - A string that specifies the subset of routes to which the cookie will apply. `Optional`
* `domain` - A string that specifies the domain for which the cookie is valid. `Optional`
* `secure` - A bool indicating that the cookie will only be sent to the server if request is made using SSL and the HTTPS protocol. `Optional`
* `httponly` - A bool indicating that the cookie cannot be accessed via JavaScript through `Document.cookie` property, the `XMLHttpRequest` or `Request` APIs. `Optional`
* `samesite` - A string that specifies the samesite strategy for the cookie. Valid values are `'lax'`, `'strict'` and `'none'`. Defaults to `'lax'`. `Optional`

#### Delete Cookie

Conversely, Starlette also provides a `delete_cookie` method to manually expire a set cookie.

Signature: `Response.delete_cookie(key, path='/', domain=None)`


### HTMLResponse

Takes some text or bytes and returns an HTML response.

```python
from starlette.responses import HTMLResponse


async def app(scope, receive, send):
    assert scope['type'] == 'http'
    response = HTMLResponse('<html><body><h1>Hello, world!</h1></body></html>')
    await response(scope, receive, send)
```

### PlainTextResponse

Takes some text or bytes and returns a plain text response.

```python
from starlette.responses import PlainTextResponse


async def app(scope, receive, send):
    assert scope['type'] == 'http'
    response = PlainTextResponse('Hello, world!')
    await response(scope, receive, send)
```

### JSONResponse

Takes some data and returns an `application/json` encoded response.

```python
from starlette.responses import JSONResponse


async def app(scope, receive, send):
    assert scope['type'] == 'http'
    response = JSONResponse({'hello': 'world'})
    await response(scope, receive, send)
```

#### Custom JSON serialization

If you need fine-grained control over JSON serialization, you can subclass
`JSONResponse` and override the `render` method.

For example, if you wanted to use a third-party JSON library such as
[orjson](https://pypi.org/project/orjson/):

```python
from typing import Any

import orjson
from starlette.responses import JSONResponse


class OrjsonResponse(JSONResponse):
    def render(self, content: Any) -> bytes:
        return orjson.dumps(content)
```

In general you *probably* want to stick with `JSONResponse` by default unless
you are micro-optimising a particular endpoint or need to serialize non-standard
object types.

### RedirectResponse

Returns an HTTP redirect. Uses a 307 status code by default.

```python
from starlette.responses import PlainTextResponse, RedirectResponse


async def app(scope, receive, send):
    assert scope['type'] == 'http'
    if scope['path'] != '/':
        response = RedirectResponse(url='/')
    else:
        response = PlainTextResponse('Hello, world!')
    await response(scope, receive, send)
```

### StreamingResponse

Takes an async generator or a normal generator/iterator and streams the response body.

```python
from starlette.responses import StreamingResponse
import asyncio


async def slow_numbers(minimum, maximum):
    yield '<html><body><ul>'
    for number in range(minimum, maximum + 1):
        yield '<li>%d</li>' % number
        await asyncio.sleep(0.5)
    yield '</ul></body></html>'


async def app(scope, receive, send):
    assert scope['type'] == 'http'
    generator = slow_numbers(1, 10)
    response = StreamingResponse(generator, media_type='text/html')
    await response(scope, receive, send)
```

Have in mind that <a href="https://docs.python.org/3/glossary.html#term-file-like-object" target="_blank">file-like</a> objects (like those created by `open()`) are normal iterators. So, you can return them directly in a `StreamingResponse`.

### FileResponse

Asynchronously streams a file as the response.

Takes a different set of arguments to instantiate than the other response types:

* `path` - The filepath to the file to stream.
* `headers` - Any custom headers to include, as a dictionary.
* `media_type` - A string giving the media type. If unset, the filename or path will be used to infer a media type.
* `filename` - If set, this will be included in the response `Content-Disposition`.
* `content_disposition_type` - will be included in the response `Content-Disposition`. Can be set to "attachment" (default) or "inline".

File responses will include appropriate `Content-Length`, `Last-Modified` and `ETag` headers.

```python
from starlette.responses import FileResponse


async def app(scope, receive, send):
    assert scope['type'] == 'http'
    response = FileResponse('statics/favicon.ico')
    await response(scope, receive, send)
```

## Third party responses

#### [EventSourceResponse](https://github.com/sysid/sse-starlette)

A response class that implements [Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html). It enables event streaming from the server to the client without the complexity of websockets.

#### [baize.asgi.FileResponse](https://baize.aber.sh/asgi#fileresponse)

As a smooth replacement for Starlette [`FileResponse`](https://www.starlette.io/responses/#fileresponse), it will automatically handle [Head method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) and [Range requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests).


# websockets.md


Starlette includes a `WebSocket` class that fulfils a similar role
to the HTTP request, but that allows sending and receiving data on a websocket.

### WebSocket

Signature: `WebSocket(scope, receive=None, send=None)`

```python
from starlette.websockets import WebSocket


async def app(scope, receive, send):
    websocket = WebSocket(scope=scope, receive=receive, send=send)
    await websocket.accept()
    await websocket.send_text('Hello, world!')
    await websocket.close()
```

WebSockets present a mapping interface, so you can use them in the same
way as a `scope`.

For instance: `websocket['path']` will return the ASGI path.

#### URL

The websocket URL is accessed as `websocket.url`.

The property is actually a subclass of `str`, and also exposes all the
components that can be parsed out of the URL.

For example: `websocket.url.path`, `websocket.url.port`, `websocket.url.scheme`.

#### Headers

Headers are exposed as an immutable, case-insensitive, multi-dict.

For example: `websocket.headers['sec-websocket-version']`

#### Query Parameters

Query parameters are exposed as an immutable multi-dict.

For example: `websocket.query_params['search']`

#### Path Parameters

Router path parameters are exposed as a dictionary interface.

For example: `websocket.path_params['username']`

### Accepting the connection

* `await websocket.accept(subprotocol=None, headers=None)`

### Sending data

* `await websocket.send_text(data)`
* `await websocket.send_bytes(data)`
* `await websocket.send_json(data)`

JSON messages default to being sent over text data frames, from version 0.10.0 onwards.
Use `websocket.send_json(data, mode="binary")` to send JSON over binary data frames.

### Receiving data

* `await websocket.receive_text()`
* `await websocket.receive_bytes()`
* `await websocket.receive_json()`

May raise `starlette.websockets.WebSocketDisconnect()`.

JSON messages default to being received over text data frames, from version 0.10.0 onwards.
Use `websocket.receive_json(data, mode="binary")` to receive JSON over binary data frames.

### Iterating data

* `websocket.iter_text()`
* `websocket.iter_bytes()`
* `websocket.iter_json()`

Similar to `receive_text`, `receive_bytes`, and `receive_json` but returns an
async iterator.

```python hl_lines="7-8"
from starlette.websockets import WebSocket


async def app(scope, receive, send):
    websocket = WebSocket(scope=scope, receive=receive, send=send)
    await websocket.accept()
    async for message in websocket.iter_text():
        await websocket.send_text(f"Message text was: {message}")
    await websocket.close()
```

When `starlette.websockets.WebSocketDisconnect` is raised, the iterator will exit.

### Closing the connection

* `await websocket.close(code=1000, reason=None)`

### Sending and receiving messages

If you need to send or receive raw ASGI messages then you should use
`websocket.send()` and `websocket.receive()` rather than using the raw `send` and
`receive` callables. This will ensure that the websocket's state is kept
correctly updated.

* `await websocket.send(message)`
* `await websocket.receive()`

### Send Denial Response

If you call `websocket.close()` before calling `websocket.accept()` then
the server will automatically send a HTTP 403 error to the client.

If you want to send a different error response, you can use the
`websocket.send_denial_response()` method. This will send the response
and then close the connection.

* `await websocket.send_denial_response(response)`

This requires the ASGI server to support the WebSocket Denial Response
extension. If it is not supported a `RuntimeError` will be raised.


# routing.md

## HTTP Routing

Starlette has a simple but capable request routing system. A routing table
is defined as a list of routes, and passed when instantiating the application.

```python
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.routing import Route


async def homepage(request):
    return PlainTextResponse("Homepage")

async def about(request):
    return PlainTextResponse("About")


routes = [
    Route("/", endpoint=homepage),
    Route("/about", endpoint=about),
]

app = Starlette(routes=routes)
```

The `endpoint` argument can be one of:

* A regular function or async function, which accepts a single `request`
argument and which should return a response.
* A class that implements the ASGI interface, such as Starlette's [HTTPEndpoint](endpoints.md#httpendpoint).

## Path Parameters

Paths can use URI templating style to capture path components.

```python
Route('/users/{username}', user)
```
By default this will capture characters up to the end of the path or the next `/`.

You can use convertors to modify what is captured. The available convertors are:

* `str` returns a string, and is the default.
* `int` returns a Python integer.
* `float` returns a Python float.
* `uuid` return a Python `uuid.UUID` instance.
* `path` returns the rest of the path, including any additional `/` characters.

Convertors are used by prefixing them with a colon, like so:

```python
Route('/users/{user_id:int}', user)
Route('/floating-point/{number:float}', floating_point)
Route('/uploaded/{rest_of_path:path}', uploaded)
```

If you need a different converter that is not defined, you can create your own.
See below an example on how to create a `datetime` convertor, and how to register it:

```python
from datetime import datetime

from starlette.convertors import Convertor, register_url_convertor


class DateTimeConvertor(Convertor):
    regex = "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(.[0-9]+)?"

    def convert(self, value: str) -> datetime:
        return datetime.strptime(value, "%Y-%m-%dT%H:%M:%S")

    def to_string(self, value: datetime) -> str:
        return value.strftime("%Y-%m-%dT%H:%M:%S")

register_url_convertor("datetime", DateTimeConvertor())
```

After registering it, you'll be able to use it as:

```python
Route('/history/{date:datetime}', history)
```

Path parameters are made available in the request, as the `request.path_params`
dictionary.

```python
async def user(request):
    user_id = request.path_params['user_id']
    ...
```

## Handling HTTP methods

Routes can also specify which HTTP methods are handled by an endpoint:

```python
Route('/users/{user_id:int}', user, methods=["GET", "POST"])
```

By default function endpoints will only accept `GET` requests, unless specified.

## Submounting routes

In large applications you might find that you want to break out parts of the
routing table, based on a common path prefix.

```python
routes = [
    Route('/', homepage),
    Mount('/users', routes=[
        Route('/', users, methods=['GET', 'POST']),
        Route('/{username}', user),
    ])
]
```

This style allows you to define different subsets of the routing table in
different parts of your project.

```python
from myproject import users, auth

routes = [
    Route('/', homepage),
    Mount('/users', routes=users.routes),
    Mount('/auth', routes=auth.routes),
]
```

You can also use mounting to include sub-applications within your Starlette
application. For example...

```python
# This is a standalone static files server:
app = StaticFiles(directory="static")

# This is a static files server mounted within a Starlette application,
# underneath the "/static" path.
routes = [
    ...
    Mount("/static", app=StaticFiles(directory="static"), name="static")
]

app = Starlette(routes=routes)
```

## Reverse URL lookups

You'll often want to be able to generate the URL for a particular route,
such as in cases where you need to return a redirect response.

* Signature: `url_for(name, **path_params) -> URL`

```python
routes = [
    Route("/", homepage, name="homepage")
]

# We can use the following to return a URL...
url = request.url_for("homepage")
```

URL lookups can include path parameters...

```python
routes = [
    Route("/users/{username}", user, name="user_detail")
]

# We can use the following to return a URL...
url = request.url_for("user_detail", username=...)
```

If a `Mount` includes a `name`, then submounts should use a `{prefix}:{name}`
style for reverse URL lookups.

```python
routes = [
    Mount("/users", name="users", routes=[
        Route("/", user, name="user_list"),
        Route("/{username}", user, name="user_detail")
    ])
]

# We can use the following to return URLs...
url = request.url_for("users:user_list")
url = request.url_for("users:user_detail", username=...)
```

Mounted applications may include a `path=...` parameter.

```python
routes = [
    ...
    Mount("/static", app=StaticFiles(directory="static"), name="static")
]

# We can use the following to return URLs...
url = request.url_for("static", path="/css/base.css")
```

For cases where there is no `request` instance, you can make reverse lookups
against the application, although these will only return the URL path.

```python
url = app.url_path_for("user_detail", username=...)
```

## Host-based routing

If you want to use different routes for the same path based on the `Host` header.

Note that port is removed from the `Host` header when matching.
For example, `Host (host='example.org:3600', ...)` will be processed
even if the `Host` header contains or does not contain a port other than `3600`
(`example.org:5600`, `example.org`).
Therefore, you can specify the port if you need it for use in `url_for`.

There are several ways to connect host-based routes to your application

```python
site = Router()  # Use eg. `@site.route()` to configure this.
api = Router()  # Use eg. `@api.route()` to configure this.
news = Router()  # Use eg. `@news.route()` to configure this.

routes = [
    Host('api.example.org', api, name="site_api")
]

app = Starlette(routes=routes)

app.host('www.example.org', site, name="main_site")

news_host = Host('news.example.org', news)
app.router.routes.append(news_host)
```

URL lookups can include host parameters just like path parameters

```python
routes = [
    Host("{subdomain}.example.org", name="sub", app=Router(routes=[
        Mount("/users", name="users", routes=[
            Route("/", user, name="user_list"),
            Route("/{username}", user, name="user_detail")
        ])
    ]))
]
...
url = request.url_for("sub:users:user_detail", username=..., subdomain=...)
url = request.url_for("sub:users:user_list", subdomain=...)
```

## Route priority

Incoming paths are matched against each `Route` in order.

In cases where more that one route could match an incoming path, you should
take care to ensure that more specific routes are listed before general cases.

For example:

```python
# Don't do this: `/users/me` will never match incoming requests.
routes = [
    Route('/users/{username}', user),
    Route('/users/me', current_user),
]

# Do this: `/users/me` is tested first.
routes = [
    Route('/users/me', current_user),
    Route('/users/{username}', user),
]
```

## Working with Router instances

If you're working at a low-level you might want to use a plain `Router`
instance, rather that creating a `Starlette` application. This gives you
a lightweight ASGI application that just provides the application routing,
without wrapping it up in any middleware.

```python
app = Router(routes=[
    Route('/', homepage),
    Mount('/users', routes=[
        Route('/', users, methods=['GET', 'POST']),
        Route('/{username}', user),
    ])
])
```

## WebSocket Routing

When working with WebSocket endpoints, you should use `WebSocketRoute`
instead of the usual `Route`.

Path parameters, and reverse URL lookups for `WebSocketRoute` work the the same
as HTTP `Route`, which can be found in the HTTP [Route](#http-routing) section above.

The `endpoint` argument can be one of:

* An async function, which accepts a single `websocket` argument.
* A class that implements the ASGI interface, such as Starlette's [WebSocketEndpoint](endpoints.md#websocketendpoint).


# endpoints.md


Starlette includes the classes `HTTPEndpoint` and `WebSocketEndpoint` that provide a class-based view pattern for
handling HTTP method dispatching and WebSocket sessions.

### HTTPEndpoint

The `HTTPEndpoint` class can be used as an ASGI application:

```python
from starlette.responses import PlainTextResponse
from starlette.endpoints import HTTPEndpoint


class App(HTTPEndpoint):
    async def get(self, request):
        return PlainTextResponse(f"Hello, world!")
```

If you're using a Starlette application instance to handle routing, you can
dispatch to an `HTTPEndpoint` class. Make sure to dispatch to the class itself,
rather than to an instance of the class:

```python
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.endpoints import HTTPEndpoint
from starlette.routing import Route


class Homepage(HTTPEndpoint):
    async def get(self, request):
        return PlainTextResponse(f"Hello, world!")


class User(HTTPEndpoint):
    async def get(self, request):
        username = request.path_params['username']
        return PlainTextResponse(f"Hello, {username}")

routes = [
    Route("/", Homepage),
    Route("/{username}", User)
]

app = Starlette(routes=routes)
```

HTTP endpoint classes will respond with "405 Method not allowed" responses for any
request methods which do not map to a corresponding handler.

### WebSocketEndpoint

The `WebSocketEndpoint` class is an ASGI application that presents a wrapper around
the functionality of a `WebSocket` instance.

The ASGI connection scope is accessible on the endpoint instance via `.scope` and
has an attribute `encoding` which may optionally be set, in order to validate the expected websocket data in the `on_receive` method.

The encoding types are:

* `'json'`
* `'bytes'`
* `'text'`

There are three overridable methods for handling specific ASGI websocket message types:

* `async def on_connect(websocket, **kwargs)`
* `async def on_receive(websocket, data)`
* `async def on_disconnect(websocket, close_code)`

The `WebSocketEndpoint` can also be used with the `Starlette` application class.


# middleware.md


Starlette includes several middleware classes for adding behavior that is applied across
your entire application. These are all implemented as standard ASGI
middleware classes, and can be applied either to Starlette or to any other ASGI application.

## Using middleware

The Starlette application class allows you to include the ASGI middleware
in a way that ensures that it remains wrapped by the exception handler.

```python
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
from starlette.middleware.trustedhost import TrustedHostMiddleware

routes = ...

# Ensure that all requests include an 'example.com' or
# '*.example.com' host header, and strictly enforce https-only access.
middleware = [
    Middleware(
        TrustedHostMiddleware,
        allowed_hosts=['example.com', '*.example.com'],
    ),
    Middleware(HTTPSRedirectMiddleware)
]

app = Starlette(routes=routes, middleware=middleware)
```

Every Starlette application automatically includes two pieces of middleware by default:

* `ServerErrorMiddleware` - Ensures that application exceptions may return a custom 500 page, or display an application traceback in DEBUG mode. This is *always* the outermost middleware layer.
* `ExceptionMiddleware` - Adds exception handlers, so that particular types of expected exception cases can be associated with handler functions. For example raising `HTTPException(status_code=404)` within an endpoint will end up rendering a custom 404 page.

Middleware is evaluated from top-to-bottom, so the flow of execution in our example
application would look like this:

* Middleware
    * `ServerErrorMiddleware`
    * `TrustedHostMiddleware`
    * `HTTPSRedirectMiddleware`
    * `ExceptionMiddleware`
* Routing
* Endpoint

The following middleware implementations are available in the Starlette package:

- CORSMiddleware
- SessionMiddleware
- HTTPSRedirectMiddleware
- TrustedHostMiddleware
- GZipMiddleware
- BaseHTTPMiddleware

# lifespan.md


Starlette applications can register a lifespan handler for dealing with
code that needs to run before the application starts up, or when the application
is shutting down.

```python
import contextlib

from starlette.applications import Starlette


@contextlib.asynccontextmanager
async def lifespan(app):
    async with some_async_resource():
        print("Run at startup!")
        yield
        print("Run on shutdown!")


routes = [
    ...
]

app = Starlette(routes=routes, lifespan=lifespan)
```

Starlette will not start serving any incoming requests until the lifespan has been run.

The lifespan teardown will run once all connections have been closed, and
any in-process background tasks have completed.

Consider using [`anyio.create_task_group()`](https://anyio.readthedocs.io/en/stable/tasks.html)
for managing asynchronous tasks.

## Lifespan State

The lifespan has the concept of `state`, which is a dictionary that
can be used to share the objects between the lifespan, and the requests.

```python
import contextlib
from typing import AsyncIterator, TypedDict

import httpx
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import PlainTextResponse
from starlette.routing import Route


class State(TypedDict):
    http_client: httpx.AsyncClient


@contextlib.asynccontextmanager
async def lifespan(app: Starlette) -> AsyncIterator[State]:
    async with httpx.AsyncClient() as client:
        yield {"http_client": client}


async def homepage(request: Request) -> PlainTextResponse:
    client = request.state.http_client
    response = await client.get("https://www.example.com")
    return PlainTextResponse(response.text)


app = Starlette(
    lifespan=lifespan,
    routes=[Route("/", homepage)]
)
```

The `state` received on the requests is a **shallow** copy of the state received on the
lifespan handler.

## Running lifespan in tests

You should use `TestClient` as a context manager, to ensure that the lifespan is called.

```python
from example import app
from starlette.testclient import TestClient


def test_homepage():
    with TestClient(app) as client:
        # Application's lifespan is called on entering the block.
        response = client.get("/")
        assert response.status_code == 200

    # And the lifespan's teardown is run when exiting the block.
```


# background.md


Starlette includes a `BackgroundTask` class for in-process background tasks.

A background task should be attached to a response, and will run only once
the response has been sent.

### Background Task

Used to add a single background task to a response.

Signature: `BackgroundTask(func, *args, **kwargs)`

```python
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from starlette.background import BackgroundTask


...

async def signup(request):
    data = await request.json()
    username = data['username']
    email = data['email']
    task = BackgroundTask(send_welcome_email, to_address=email)
    message = {'status': 'Signup successful'}
    return JSONResponse(message, background=task)

async def send_welcome_email(to_address):
    ...


routes = [
    ...
    Route('/user/signup', endpoint=signup, methods=['POST'])
]

app = Starlette(routes=routes)
```

### BackgroundTasks

Used to add multiple background tasks to a response.

Signature: `BackgroundTasks(tasks=[])`

!!! important
    The tasks are executed in order. In case one of the tasks raises
    an exception, the following tasks will not get the opportunity to be executed.


# server-push.md


Starlette includes support for HTTP/2 and HTTP/3 server push, making it
possible to push resources to the client to speed up page load times.

### `Request.send_push_promise`

Used to initiate a server push for a resource. If server push is not available
this method does nothing.

Signature: `send_push_promise(path)`

* `path` - A string denoting the path of the resource.

```python
from starlette.applications import Starlette
from starlette.responses import HTMLResponse
from starlette.routing import Route, Mount
from starlette.staticfiles import StaticFiles


async def homepage(request):
    """
    Homepage which uses server push to deliver the stylesheet.
    """
    await request.send_push_promise("/static/style.css")
    return HTMLResponse(
        '<html><head><link rel="stylesheet" href="/static/style.css"/></head></html>'
    )

routes = [
    Route("/", endpoint=homepage),
    Mount("/static", StaticFiles(directory="static"), name="static")
]

app = Starlette(routes=routes)
```


# exceptions.md


Starlette allows you to install custom exception handlers to deal with
how you return responses when errors or handled exceptions occur.

```python
from starlette.applications import Starlette
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.responses import HTMLResponse


HTML_404_PAGE = ...
HTML_500_PAGE = ...


async def not_found(request: Request, exc: HTTPException):
    return HTMLResponse(content=HTML_404_PAGE, status_code=exc.status_code)

async def server_error(request: Request, exc: HTTPException):
    return HTMLResponse(content=HTML_500_PAGE, status_code=exc.status_code)


exception_handlers = {
    404: not_found,
    500: server_error
}

app = Starlette(routes=routes, exception_handlers=exception_handlers)
```

If `debug` is enabled and an error occurs, then instead of using the installed
500 handler, Starlette will respond with a traceback response.

```python
app = Starlette(debug=True, routes=routes, exception_handlers=exception_handlers)
```

As well as registering handlers for specific status codes, you can also
register handlers for classes of exceptions.

In particular you might want to override how the built-in `HTTPException` class
is handled. For example, to use JSON style responses:

```python
async def http_exception(request: Request, exc: HTTPException):
    return JSONResponse({"detail": exc.detail}, status_code=exc.status_code)

exception_handlers = {
    HTTPException: http_exception
}
```

The `HTTPException` is also equipped with the `headers` argument. Which allows the propagation
of the headers to the response class:

```python
async def http_exception(request: Request, exc: HTTPException):
    return JSONResponse(
        {"detail": exc.detail},
        status_code=exc.status_code,
        headers=exc.headers
    )
```

You might also want to override how `WebSocketException` is handled:

```python
async def websocket_exception(websocket: WebSocket, exc: WebSocketException):
    await websocket.close(code=1008)

exception_handlers = {
    WebSocketException: websocket_exception
}
```

## Errors and handled exceptions

It is important to differentiate between handled exceptions and errors.

Handled exceptions do not represent error cases. They are coerced into appropriate
HTTP responses, which are then sent through the standard middleware stack. By default
the `HTTPException` class is used to manage any handled exceptions.

Errors are any other exception that occurs within the application. These cases
should bubble through the entire middleware stack as exceptions. Any error
logging middleware should ensure that it re-raises the exception all the
way up to the server.

In practical terms, the error handled used is `exception_handler[500]` or `exception_handler[Exception]`.
Both keys `500` and `Exception` can be used. See below:

```python
async def handle_error(request: Request, exc: HTTPException):
    # Perform some logic
    return JSONResponse({"detail": exc.detail}, status_code=exc.status_code)

exception_handlers = {
    Exception: handle_error  # or "500: handle_error"
}
```

It's important to notice that in case a [`BackgroundTask`](https://www.starlette.io/background/) raises an exception,
it will be handled by the `handle_error` function, but at that point, the response was already sent. In other words,
the response created by `handle_error` will be discarded. In case the error happens before the response was sent, then
it will use the response object - in the above example, the returned `JSONResponse`.

In order to deal with this behaviour correctly, the middleware stack of a
`Starlette` application is configured like this:

* `ServerErrorMiddleware` - Returns 500 responses when server errors occur.
* Installed middleware
* `ExceptionMiddleware` - Deals with handled exceptions, and returns responses.
* Router
* Endpoints

## HTTPException

The `HTTPException` class provides a base class that you can use for any
handled exceptions. The `ExceptionMiddleware` implementation defaults to
returning plain-text HTTP responses for any `HTTPException`.

* `HTTPException(status_code, detail=None, headers=None)`

You should only raise `HTTPException` inside routing or endpoints. Middleware
classes should instead just return appropriate responses directly.

## WebSocketException

You can use the `WebSocketException` class to raise errors inside of WebSocket endpoints.

* `WebSocketException(code=1008, reason=None)`

You can set any code valid as defined [in the specification](https://tools.ietf.org/html/rfc6455#section-7.4.1).


# testclient.md


The test client allows you to make requests against your ASGI application,
using the `httpx` library.

```python
from starlette.responses import HTMLResponse
from starlette.testclient import TestClient


async def app(scope, receive, send):
    assert scope['type'] == 'http'
    response = HTMLResponse('<html><body>Hello, world!</body></html>')
    await response(scope, receive, send)


def test_app():
    client = TestClient(app)
    response = client.get('/')
    assert response.status_code == 200
```

The test client exposes the same interface as any other `httpx` session.
In particular, note that the calls to make a request are just standard
function calls, not awaitables.

You can use any of `httpx` standard API, such as authentication, session
cookies handling, or file uploads.

For example, to set headers on the TestClient you can do:

```python
client = TestClient(app)

# Set headers on the client for future requests
client.headers = {"Authorization": "..."}
response = client.get("/")

# Set headers for each request separately
response = client.get("/", headers={"Authorization": "..."})
```

And for example to send files with the TestClient:

```python
client = TestClient(app)

# Send a single file
with open("example.txt", "rb") as f:
    response = client.post("/form", files={"file": f})

# Send multiple files
with open("example.txt", "rb") as f1:
    with open("example.png", "rb") as f2:
        files = {"file1": f1, "file2": ("filename", f2, "image/png")}
        response = client.post("/form", files=files)
```

### Testing WebSocket sessions

You can also test websocket sessions with the test client.

The `httpx` library will be used to build the initial handshake, meaning you
can use the same authentication options and other headers between both http and
websocket testing.

```python
from starlette.testclient import TestClient
from starlette.websockets import WebSocket


async def app(scope, receive, send):
    assert scope['type'] == 'websocket'
    websocket = WebSocket(scope, receive=receive, send=send)
    await websocket.accept()
    await websocket.send_text('Hello, world!')
    await websocket.close()


def test_app():
    client = TestClient(app)
    with client.websocket_connect('/') as websocket:
        data = websocket.receive_text()
        assert data == 'Hello, world!'
```

#### Sending data

* `.send_text(data)` - Send the given text to the application.
* `.send_bytes(data)` - Send the given bytes to the application.
* `.send_json(data, mode="text")` - Send the given data to the application. Use `mode="binary"` to send JSON over binary data frames.

#### Receiving data

* `.receive_text()` - Wait for incoming text sent by the application and return it.
* `.receive_bytes()` - Wait for incoming bytestring sent by the application and return it.
* `.receive_json(mode="text")` - Wait for incoming json data sent by the application and return it. Use `mode="binary"` to receive JSON over binary data frames.

May raise `starlette.websockets.WebSocketDisconnect`.

#### Closing the connection

* `.close(code=1000)` - Perform a client-side close of the websocket connection.

### Asynchronous tests

Sometimes you will want to do async things outside of your application.
For example, you might want to check the state of your database after calling your app using your existing async database client / infrastructure.

For these situations, using `TestClient` is difficult because it creates it's own event loop and async resources (like a database connection) often cannot be shared across event loops.
The simplest way to work around this is to just make your entire test async and use an async client, like [httpx.AsyncClient].

Here is an example of such a test:

```python
from httpx import AsyncClient
from starlette.applications import Starlette
from starlette.routing import Route
from starlette.requests import Request
from starlette.responses import PlainTextResponse


def hello(request: Request) -> PlainTextResponse:
    return PlainTextResponse("Hello World!")


app = Starlette(routes=[Route("/", hello)])


# if you're using pytest, you'll need to to add an async marker like:
# @pytest.mark.anyio  # using https://github.com/agronholm/anyio
# or install and configure pytest-asyncio (https://github.com/pytest-dev/pytest-asyncio)
async def test_app() -> None:
    # note: you _must_ set `base_url` for relative urls like "/" to work
    async with AsyncClient(app=app, base_url="http://testserver") as client:
        r = await client.get("/")
        assert r.status_code == 200
        assert r.text == "Hello World!"
```</doc><doc title="JS App Walkthrough" desc="An end-to-end walkthrough of a complete FastHTML app, including deployment to railway."># JS App Walkthrough



## Installation

You’ll need the following software to complete the tutorial, read on for
specific installation instructions:

1.  Python
2.  A Python package manager such as pip (which normally comes with
    Python) or uv
3.  FastHTML
4.  Web browser
5.  Railway.app account

If you haven’t worked with Python before, we recommend getting started
with [Miniconda](https://docs.anaconda.com/miniconda/).

Note that you will only need to follow the steps in the installation
section once per environment. If you create a new repo, you won’t need
to redo these.

### Install FastHTML

For Mac, Windows and Linux, enter:

``` sh
pip install python-fasthtml
```

## First steps

By the end of this section you’ll have your own FastHTML website with
tests deployed to railway.app.

### Create a hello world

Create a new folder to organize all the files for your project. Inside
this folder, create a file called `main.py` and add the following code
to it:

<div class="code-with-filename">

**main.py**

``` python
from fasthtml.common import *

app = FastHTML()
rt = app.route

@rt('/')
def get():
    return 'Hello, world!'

serve()
```

</div>

Finally, run `python main.py` in your terminal and open your browser to
the ‘Link’ that appears.

### QuickDraw: A FastHTML Adventure 🎨✨

The end result of this tutorial will be QuickDraw, a real-time
collaborative drawing app using FastHTML. Here is what the final site
will look like:

<figure>
<img src="imgs/quickdraw.png" alt="QuickDraw" />
<figcaption aria-hidden="true">QuickDraw</figcaption>
</figure>

#### Drawing Rooms

Drawing rooms are the core concept of our application. Each room
represents a separate drawing space where a user can let their inner
Picasso shine. Here’s a detailed breakdown:

1.  Room Creation and Storage

<div class="code-with-filename">

**main.py**

``` python
db = database('data/drawapp.db')
rooms = db.t.rooms
if rooms not in db.t:
    rooms.create(id=int, name=str, created_at=str, pk='id')
Room = rooms.dataclass()

@patch
def __ft__(self:Room):
    return Li(A(self.name, href=f"/rooms/{self.id}"))
```

</div>

Or you can use our `fast_app` function to create a FastHTML app with a
SQLite database and dataclass in one line:

<div class="code-with-filename">

**main.py**

``` python
def render(room):
    return Li(A(room.name, href=f"/rooms/{room.id}"))

app,rt,rooms,Room = fast_app('data/drawapp.db', render=render, id=int, name=str, created_at=str, pk='id')
```

</div>

We are specifying a render function to convert our dataclass into HTML,
which is the same as extending the `__ft__` method from the `patch`
decorator we used before. We will use this method for the rest of the
tutorial since it is a lot cleaner and easier to read.

- We’re using a SQLite database (via FastLite) to store our rooms.
- Each room has an id (integer), a name (string), and a created_at
  timestamp (string).
- The Room dataclass is automatically generated based on this structure.

2.  Creating a room

<div class="code-with-filename">

**main.py**

``` python
@rt("/")
def get():
    # The 'Input' id defaults to the same as the name, so you can omit it if you wish
    create_room = Form(Input(id="name", name="name", placeholder="New Room Name"),
                       Button("Create Room"),
                       hx_post="/rooms", hx_target="#rooms-list", hx_swap="afterbegin")
    rooms_list = Ul(*rooms(order_by='id DESC'), id='rooms-list')
    return Titled("DrawCollab", 
                  H1("DrawCollab"),
                  create_room, rooms_list)

@rt("/rooms")
async def post(room:Room):
    room.created_at = datetime.now().isoformat()
    return rooms.insert(room)
```

</div>

- When a user submits the “Create Room” form, this route is called.
- It creates a new Room object, sets the creation time, and inserts it
  into the database.
- It returns an HTML list item with a link to the new room, which is
  dynamically added to the room list on the homepage thanks to HTMX.

3.  Let’s give our rooms shape

<div class="code-with-filename">

**main.py**

``` python
@rt("/rooms/{id}")
async def get(id:int):
    room = rooms[id]
    return Titled(f"Room: {room.name}", H1(f"Welcome to {room.name}"), A(Button("Leave Room"), href="/"))
```

</div>

- This route renders the interface for a specific room.
- It fetches the room from the database and renders a title, heading,
  and paragraph.

Here is the full code so far:

<div class="code-with-filename">

**main.py**

``` python
from fasthtml.common import *
from datetime import datetime

def render(room):
    return Li(A(room.name, href=f"/rooms/{room.id}"))

app,rt,rooms,Room = fast_app('data/drawapp.db', render=render, id=int, name=str, created_at=str, pk='id')

@rt("/")
def get():
    create_room = Form(Input(id="name", name="name", placeholder="New Room Name"),
                       Button("Create Room"),
                       hx_post="/rooms", hx_target="#rooms-list", hx_swap="afterbegin")
    rooms_list = Ul(*rooms(order_by='id DESC'), id='rooms-list')
    return Titled("DrawCollab", create_room, rooms_list)

@rt("/rooms")
async def post(room:Room):
    room.created_at = datetime.now().isoformat()
    return rooms.insert(room)

@rt("/rooms/{id}")
async def get(id:int):
    room = rooms[id]
    return Titled(f"Room: {room.name}", H1(f"Welcome to {room.name}"), A(Button("Leave Room"), href="/"))

serve()
```

</div>

Now run `python main.py` in your terminal and open your browser to the
‘Link’ that appears. You should see a page with a form to create a new
room and a list of existing rooms.

#### The Canvas - Let’s Get Drawing! 🖌️

Time to add the actual drawing functionality. We’ll use Fabric.js for
this:

<div class="code-with-filename">

**main.py**

``` python
# ... (keep the previous imports and database setup)

@rt("/rooms/{id}")
async def get(id:int):
    room = rooms[id]
    canvas = Canvas(id="canvas", width="800", height="600")
    color_picker = Input(type="color", id="color-picker", value="#3CDD8C")
    brush_size = Input(type="range", id="brush-size", min="1", max="50", value="10")
    
    js = """
    var canvas = new fabric.Canvas('canvas');
    canvas.isDrawingMode = true;
    canvas.freeDrawingBrush.color = '#3CDD8C';
    canvas.freeDrawingBrush.width = 10;
    
    document.getElementById('color-picker').onchange = function() {
        canvas.freeDrawingBrush.color = this.value;
    };
    
    document.getElementById('brush-size').oninput = function() {
        canvas.freeDrawingBrush.width = parseInt(this.value, 10);
    };
    """
    
    return Titled(f"Room: {room.name}",
                  A(Button("Leave Room"), href="/"),
                  canvas,
                  Div(color_picker, brush_size),
                  Script(src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"),
                  Script(js))

# ... (keep the serve() part)
```

</div>

Now we’ve got a drawing canvas! FastHTML makes it easy to include
external libraries and add custom JavaScript.

#### Saving and Loading Canvases 💾

Now that we have a working drawing canvas, let’s add the ability to save
and load drawings. We’ll modify our database schema to include a
`canvas_data` field, and add new routes for saving and loading canvas
data. Here’s how we’ll update our code:

1.  Modify the database schema:

<div class="code-with-filename">

**main.py**

``` python
app,rt,rooms,Room = fast_app('data/drawapp.db', render=render, id=int, name=str, created_at=str, canvas_data=str, pk='id')
```

</div>

2.  Add a save button that grabs the canvas’ state and sends it to the
    server:

<div class="code-with-filename">

**main.py**

``` python
@rt("/rooms/{id}")
async def get(id:int):
    room = rooms[id]
    canvas = Canvas(id="canvas", width="800", height="600")
    color_picker = Input(type="color", id="color-picker", value="#3CDD8C")
    brush_size = Input(type="range", id="brush-size", min="1", max="50", value="10")
    save_button = Button("Save Canvas", id="save-canvas", hx_post=f"/rooms/{id}/save", hx_vals="js:{canvas_data: JSON.stringify(canvas.toJSON())}")
    # ... (rest of the function remains the same)
```

</div>

3.  Add routes for saving and loading canvas data:

<div class="code-with-filename">

**main.py**

``` python
@rt("/rooms/{id}/save")
async def post(id:int, canvas_data:str):
    rooms.update({'canvas_data': canvas_data}, id)
    return "Canvas saved successfully"

@rt("/rooms/{id}/load")
async def get(id:int):
    room = rooms[id]
    return room.canvas_data if room.canvas_data else "{}"
```

</div>

4.  Update the JavaScript to load existing canvas data:

<div class="code-with-filename">

**main.py**

``` javascript
js = f"""
    var canvas = new fabric.Canvas('canvas');
    canvas.isDrawingMode = true;
    canvas.freeDrawingBrush.color = '#3CDD8C';
    canvas.freeDrawingBrush.width = 10;
    // Load existing canvas data
    fetch(`/rooms/{id}/load`)
    .then(response => response.json())
    .then(data => {{
        if (data && Object.keys(data).length > 0) {{
            canvas.loadFromJSON(data, canvas.renderAll.bind(canvas));
        }}
    }});
    
    // ... (rest of the JavaScript remains the same)
"""
```

</div>

With these changes, users can now save their drawings and load them when
they return to the room. The canvas data is stored as a JSON string in
the database, allowing for easy serialization and deserialization. Try
it out! Create a new room, make a drawing, save it, and then reload the
page. You should see your drawing reappear, ready for further editing.

Here is the completed code:

<div class="code-with-filename">

**main.py**

``` python
from fasthtml.common import *
from datetime import datetime

def render(room):
    return Li(A(room.name, href=f"/rooms/{room.id}"))

app,rt,rooms,Room = fast_app('data/drawapp.db', render=render, id=int, name=str, created_at=str, canvas_data=str, pk='id')

@rt("/")
def get():
    create_room = Form(Input(id="name", name="name", placeholder="New Room Name"),
                       Button("Create Room"),
                       hx_post="/rooms", hx_target="#rooms-list", hx_swap="afterbegin")
    rooms_list = Ul(*rooms(order_by='id DESC'), id='rooms-list')
    return Titled("QuickDraw", 
                  create_room, rooms_list)

@rt("/rooms")
async def post(room:Room):
    room.created_at = datetime.now().isoformat()
    return rooms.insert(room)

@rt("/rooms/{id}")
async def get(id:int):
    room = rooms[id]
    canvas = Canvas(id="canvas", width="800", height="600")
    color_picker = Input(type="color", id="color-picker", value="#000000")
    brush_size = Input(type="range", id="brush-size", min="1", max="50", value="10")
    save_button = Button("Save Canvas", id="save-canvas", hx_post=f"/rooms/{id}/save", hx_vals="js:{canvas_data: JSON.stringify(canvas.toJSON())}")

    js = f"""
    var canvas = new fabric.Canvas('canvas');
    canvas.isDrawingMode = true;
    canvas.freeDrawingBrush.color = '#000000';
    canvas.freeDrawingBrush.width = 10;

    // Load existing canvas data
    fetch(`/rooms/{id}/load`)
    .then(response => response.json())
    .then(data => {{
        if (data && Object.keys(data).length > 0) {{
            canvas.loadFromJSON(data, canvas.renderAll.bind(canvas));
        }}
    }});
    
    document.getElementById('color-picker').onchange = function() {{
        canvas.freeDrawingBrush.color = this.value;
    }};
    
    document.getElementById('brush-size').oninput = function() {{
        canvas.freeDrawingBrush.width = parseInt(this.value, 10);
    }};
    """
    
    return Titled(f"Room: {room.name}",
                  A(Button("Leave Room"), href="/"),
                  canvas,
                  Div(color_picker, brush_size, save_button),
                  Script(src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"),
                  Script(js))

@rt("/rooms/{id}/save")
async def post(id:int, canvas_data:str):
    rooms.update({'canvas_data': canvas_data}, id)
    return "Canvas saved successfully"

@rt("/rooms/{id}/load")
async def get(id:int):
    room = rooms[id]
    return room.canvas_data if room.canvas_data else "{}"

serve()
```

</div>

### Deploying to Railway

You can deploy your website to a number of hosting providers, for this
tutorial we’ll be using Railway. To get started, make sure you create an
[account](https://railway.app/) and install the [Railway
CLI](https://docs.railway.app/guides/cli). Once installed, make sure to
run `railway login` to log in to your account.

To make deploying your website as easy as possible, FastHTMl comes with
a built in CLI tool that will handle most of the deployment process for
you. To deploy your website, run the following command in your terminal
in the root directory of your project:

``` sh
fh_railway_deploy quickdraw
```

<div>

> **Note**
>
> Your app must be located in a `main.py` file for this to work.

</div>

### Conclusion: You’re a FastHTML Artist Now! 🎨🚀

Congratulations! You’ve just built a sleek, interactive web application
using FastHTML. Let’s recap what we’ve learned:

1.  FastHTML allows you to create dynamic web apps with minimal code.
2.  We used FastHTML’s routing system to handle different pages and
    actions.
3.  We integrated with a SQLite database to store room information and
    canvas data.
4.  We utilized Fabric.js to create an interactive drawing canvas.
5.  We implemented features like color picking, brush size adjustment,
    and canvas saving.
6.  We used HTMX for seamless, partial page updates without full
    reloads.
7.  We learned how to deploy our FastHTML application to Railway for
    easy hosting.

You’ve taken your first steps into the world of FastHTML development.
From here, the possibilities are endless! You could enhance the drawing
app further by adding features like:

- Implementing different drawing tools (e.g., shapes, text)
- Adding user authentication
- Creating a gallery of saved drawings
- Implementing real-time collaborative drawing using WebSockets

Whatever you choose to build next, FastHTML has got your back. Now go
forth and create something awesome! Happy coding! 🖼️🚀</doc><doc title="FastHTML by Example" desc="A collection of 4 FastHTML apps showcasing idiomatic use of FastHTML and HTMX patterns."># FastHTML By Example



This tutorial provides an alternate introduction to FastHTML by building
out example applications. We also illustrate how to use FastHTML
foundations to create custom web apps. Finally, this document serves as
minimal context for a LLM to turn it into a FastHTML assistant.

Let’s get started.

## FastHTML Basics

FastHTML is *just Python*. You can install it with
`pip install python-fasthtml`. Extensions/components built for it can
likewise be distributed via PyPI or as simple Python files.

The core usage of FastHTML is to define routes, and then to define what
to do at each route. This is similar to the
[FastAPI](https://fastapi.tiangolo.com/) web framework (in fact we
implemented much of the functionality to match the FastAPI usage
examples), but where FastAPI focuses on returning JSON data to build
APIs, FastHTML focuses on returning HTML data.

Here’s a simple FastHTML app that returns a “Hello, World” message:

``` python
from fasthtml.common import FastHTML, serve

app = FastHTML()

@app.get("/")
def home():
    return "<h1>Hello, World</h1>"

serve()
```

To run this app, place it in a file, say `app.py`, and then run it with
`python app.py`.

    INFO:     Will watch for changes in these directories: ['/home/jonathan/fasthtml-example']
    INFO:     Uvicorn running on http://127.0.0.1:5001 (Press CTRL+C to quit)
    INFO:     Started reloader process [871942] using WatchFiles
    INFO:     Started server process [871945]
    INFO:     Waiting for application startup.
    INFO:     Application startup complete.

If you navigate to <http://127.0.0.1:5001> in a browser, you’ll see your
“Hello, World”. If you edit the `app.py` file and save it, the server
will reload and you’ll see the updated message when you refresh the page
in your browser.

## Constructing HTML

Notice we wrote some HTML in the previous example. We don’t want to do
that! Some web frameworks require that you learn HTML, CSS, JavaScript
AND some templating language AND python. We want to do as much as
possible with just one language. Fortunately, the Python module
[fastcore.xml](https://fastcore.fast.ai/xml.html) has all we need for
constructing HTML from Python, and FastHTML includes all the tags you
need to get started. For example:

``` python
from fasthtml.common import *
page = Html(
    Head(Title('Some page')),
    Body(Div('Some text, ', A('A link', href='https://example.com'), Img(src="https://placehold.co/200"), cls='myclass')))
print(to_xml(page))
```

    <!doctype html></!doctype>

    <html>
      <head>
        <title>Some page</title>
      </head>
      <body>
        <div class="myclass">
    Some text, 
          <a href="https://example.com">A link</a>
          <img src="https://placehold.co/200">
        </div>
      </body>
    </html>

``` python
show(page)
```

<!doctype html></!doctype>
&#10;<html>
  <head>
    <title>Some page</title>
  </head>
  <body>
    <div class="myclass">
Some text, 
      <a href="https://example.com">A link</a>
      <img src="https://placehold.co/200">
    </div>
  </body>
</html>

If that `import *` worries you, you can always import only the tags you
need.

FastHTML is smart enough to know about fastcore.xml, and so you don’t
need to use the `to_xml` function to convert your FT objects to HTML.
You can just return them as you would any other Python object. For
example, if we modify our previous example to use fastcore.xml, we can
return an FT object directly:

``` python
from fasthtml.common import *
app = FastHTML()

@app.get("/")
def home():
    page = Html(
        Head(Title('Some page')),
        Body(Div('Some text, ', A('A link', href='https://example.com'), Img(src="https://placehold.co/200"), cls='myclass')))
    return page

serve()
```

This will render the HTML in the browser.

For debugging, you can right-click on the rendered HTML in the browser
and select “Inspect” to see the underlying HTML that was generated.
There you’ll also find the ‘network’ tab, which shows you the requests
that were made to render the page. Refresh and look for the request to
`127.0.0.1` - and you’ll see it’s just a `GET` request to `/`, and the
response body is the HTML you just returned.

<div>

> **Live Reloading**
>
> You can also enable [live reloading](../ref/live_reload.ipynb) so you
> don’t have to manually refresh your browser to view updates.

</div>

You can also use Starlette’s `TestClient` to try it out in a notebook:

``` python
from starlette.testclient import TestClient
client = TestClient(app)
r = client.get("/")
print(r.text)
```

    <html>
      <head><title>Some page</title>
    </head>
      <body><div class="myclass">
    Some text, 
      <a href="https://example.com">A link</a>
      <img src="https://placehold.co/200">
    </div>
    </body>
    </html>

FastHTML wraps things in an Html tag if you don’t do it yourself (unless
the request comes from htmx, in which case you get the element
directly). See [FT objects and HTML](#ft-objects-and-html) for more on
creating custom components or adding HTML rendering to existing Python
objects. To give the page a non-default title, return a Title before
your main content:

``` python
app = FastHTML()

@app.get("/")
def home():
    return Title("Page Demo"), Div(H1('Hello, World'), P('Some text'), P('Some more text'))

client = TestClient(app)
print(client.get("/").text)
```

    <!doctype html></!doctype>

    <html>
      <head>
        <title>Page Demo</title>
        <meta charset="utf-8"></meta>
        <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"></meta>
        <script src="https://unpkg.com/htmx.org@next/dist/htmx.min.js"></script>
        <script src="https://cdn.jsdelivr.net/gh/answerdotai/surreal@1.3.0/surreal.js"></script>
        <script src="https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js"></script>
      </head>
      <body>
    <div>
      <h1>Hello, World</h1>
      <p>Some text</p>
      <p>Some more text</p>
    </div>
      </body>
    </html>

We’ll use this pattern often in the examples to follow.

## Defining Routes

The HTTP protocol defines a number of methods (‘verbs’) to send requests
to a server. The most common are GET, POST, PUT, DELETE, and HEAD. We
saw ‘GET’ in action before - when you navigate to a URL, you’re making a
GET request to that URL. We can do different things on a route for
different HTTP methods. For example:

``` python
@app.route("/", methods='get')
def home():
    return H1('Hello, World')

@app.route("/", methods=['post', 'put'])
def post_or_put():
    return "got a POST or PUT request"
```

This says that when someone navigates to the root URL “/” (i.e. sends a
GET request), they will see the big “Hello, World” heading. When someone
submits a POST or PUT request to the same URL, the server should return
the string “got a post or put request”.

<div>

> **Test the POST request**
>
> You can test the POST request with
> `curl -X POST http://127.0.0.1:8000 -d "some data"`. This sends some
> data to the server, you should see the response “got a post or put
> request” printed in the terminal.

</div>

There are a few other ways you can specify the route+method - FastHTML
has `.get`, `.post`, etc. as shorthand for
`route(..., methods=['get'])`, etc.

``` python
@app.get("/")
def my_function():
    return "Hello World from a GET request"
```

Or you can use the `@rt` decorator without a method but specify the
method with the name of the function. For example:

``` python
rt = app.route

@rt("/")
def post():
    return "Hello World from a POST request"
```

``` python
client.post("/").text
```

    'Hello World from a POST request'

You’re welcome to pick whichever style you prefer. Using routes lets you
show different content on different pages - ‘/home’, ‘/about’ and so on.
You can also respond differently to different kinds of requests to the
same route, as shown above. You can also pass data via the route:

<div class="panel-tabset">

## `@app.get`

``` python
@app.get("/greet/{nm}")
def greet(nm:str):
    return f"Good day to you, {nm}!"

client.get("/greet/Dave").text
```

    'Good day to you, Dave!'

## `@rt`

``` python
@rt("/greet/{nm}")
def get(nm:str):
    return f"Good day to you, {nm}!"

client.get("/greet/Dave").text
```

    'Good day to you, Dave!'

</div>

More on this in the [More on Routing and Request
Parameters](#more-on-routing-and-request-parameters) section, which goes
deeper into the different ways to get information from a request.

## Styling Basics

Plain HTML probably isn’t quite what you imagine when you visualize your
beautiful web app. CSS is the go-to language for styling HTML. But
again, we don’t want to learn extra languages unless we absolutely have
to! Fortunately, there are ways to get much more visually appealing
sites by relying on the hard work of others, using existing CSS
libraries. One of our favourites is [PicoCSS](https://picocss.com/). A
common way to add CSS files to web pages is to use a
[`<link>`](https://www.w3schools.com/tags/tag_link.asp) tag inside your
[HTML header](https://www.w3schools.com/tags/tag_header.asp), like this:

``` html
<header>
    ...
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@latest/css/pico.min.css">
</header>
```

For convenience, FastHTML already defines a Pico component for you with
`picolink`:

``` python
print(to_xml(picolink))
```

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@latest/css/pico.min.css">

    <style>:root { --pico-font-size: 100%; }</style>

<div>

> **Note**
>
> `picolink` also includes a `<style>` tag, as we found that setting the
> font-size to 100% to be a good default. We show you how to override
> this below.

</div>

Since we typically want CSS styling on all pages of our app, FastHTML
lets you define a shared HTML header with the `hdrs` argument as shown
below:

``` python
from fasthtml.common import *
css = Style(':root {--pico-font-size:90%,--pico-font-family: Pacifico, cursive;}')
app = FastHTML(hdrs=(picolink, css))

@app.route("/")
def get():
    return (Title("Hello World"), 
            Main(H1('Hello, World'), cls="container"))
```

Line 2  
Custom styling to override the pico defaults

Line 3  
Define shared headers for all pages

Line 8  
As per the [pico docs](https://picocss.com/docs), we put all of our
content inside a `<main>` tag with a class of `container`:

<div>

> **Returning Tuples**
>
> We’re returning a tuple here (a title and the main page). Returning a
> tuple, list, `FT` object, or an object with a `__ft__` method tells
> FastHTML to turn the main body into a full HTML page that includes the
> headers (including the pico link and our custom css) which we passed
> in. This only occurs if the request isn’t from HTMX (for HTMX requests
> we need only return the rendered components).

</div>

You can check out the Pico [examples](https://picocss.com/examples) page
to see how different elements will look. If everything is working, the
page should now render nice text with our custom font, and it should
respect the user’s light/dark mode preferences too.

If you want to [override the default
styles](https://picocss.com/docs/css-variables) or add more custom CSS,
you can do so by adding a `<style>` tag to the headers as shown above.
So you are allowed to write CSS to your heart’s content - we just want
to make sure you don’t necessarily have to! Later on we’ll see examples
using other component libraries and tailwind css to do more fancy
styling things, along with tips to get an LLM to write all those fiddly
bits so you don’t have to.

## Web Page -\> Web App

Showing content is all well and good, but we typically expect a bit more
*interactivity* from something calling itself a web app! So, let’s add a
few different pages, and use a form to let users add messages to a list:

``` python
app = FastHTML()
messages = ["This is a message, which will get rendered as a paragraph"]

@app.get("/")
def home():
    return Main(H1('Messages'), 
                *[P(msg) for msg in messages],
                A("Link to Page 2 (to add messages)", href="/page2"))

@app.get("/page2")
def page2():
    return Main(P("Add a message with the form below:"),
                Form(Input(type="text", name="data"),
                     Button("Submit"),
                     action="/", method="post"))

@app.post("/")
def add_message(data:str):
    messages.append(data)
    return home()
```

We re-render the entire homepage to show the newly added message. This
is fine, but modern web apps often don’t re-render the entire page, they
just update a part of the page. In fact even very complicated
applications are often implemented as ‘Single Page Apps’ (SPAs). This is
where HTMX comes in.

## HTMX

[HTMX](https://htmx.org/) addresses some key limitations of HTML. In
vanilla HTML, links can trigger a GET request to show a new page, and
forms can send requests containing data to the server. A lot of ‘Web
1.0’ design revolved around ways to use these to do everything we
wanted. But why should only *some* elements be allowed to trigger
requests? And why should we refresh the *entire page* with the result
each time one does? HTMX extends HTML to allow us to trigger requests
from *any* element on all kinds of events, and to update a part of the
page without refreshing the entire page. It’s a powerful tool for
building modern web apps.

It does this by adding attributes to HTML tags to make them do things.
For example, here’s a page with a counter and a button that increments
it:

``` python
app = FastHTML()

count = 0

@app.get("/")
def home():
    return Title("Count Demo"), Main(
        H1("Count Demo"),
        P(f"Count is set to {count}", id="count"),
        Button("Increment", hx_post="/increment", hx_target="#count", hx_swap="innerHTML")
    )

@app.post("/increment")
def increment():
    print("incrementing")
    global count
    count += 1
    return f"Count is set to {count}"
```

The button triggers a POST request to `/increment` (since we set
`hx_post="/increment"`), which increments the count and returns the new
count. The `hx_target` attribute tells HTMX where to put the result. If
no target is specified it replaces the element that triggered the
request. The `hx_swap` attribute specifies how it adds the result to the
page. Useful options are:

- *`innerHTML`*: Replace the target element’s content with the result.
- *`outerHTML`*: Replace the target element with the result.
- *`beforebegin`*: Insert the result before the target element.
- *`beforeend`*: Insert the result inside the target element, after its
  last child.
- *`afterbegin`*: Insert the result inside the target element, before
  its first child.
- *`afterend`*: Insert the result after the target element.

You can also use an hx_swap of `delete` to delete the target element
regardless of response, or of `none` to do nothing.

By default, requests are triggered by the “natural” event of an
element - click in the case of a button (and most other elements). You
can also specify different triggers, along with various modifiers - see
the [HTMX docs](https://htmx.org/docs/#triggers) for more.

This pattern of having elements trigger requests that modify or replace
other elements is a key part of the HTMX philosophy. It takes a little
getting used to, but once mastered it is extremely powerful.

### Replacing Elements Besides the Target

Sometimes having a single target is not enough, and we’d like to specify
some additional elements to update or remove. In these cases, returning
elements with an id that matches the element to be replaced and
`hx_swap_oob='true'` will replace those elements too. We’ll use this in
the next example to clear an input field when we submit a form.

## Full Example \#1 - ToDo App

The canonical demo web app! A TODO list. Rather than create yet another
variant for this tutorial, we recommend starting with this video
tutorial from Jeremy:

<https://www.youtube.com/embed/Auqrm7WFc0I>

<figure>
<img src="by_example_files/figure-commonmark/cell-53-1-image.png"
alt="image.png" />
<figcaption aria-hidden="true">image.png</figcaption>
</figure>

We’ve made a number of variants of this app - so in addition to the
version shown in the video you can browse
[this](https://github.com/AnswerDotAI/fasthtml-tut) series of examples
with increasing complexity, the heavily-commented [“idiomatic” version
here](https://github.com/AnswerDotAI/fasthtml/blob/main/examples/adv_app.py),
and the
[example](https://github.com/AnswerDotAI/fasthtml-example/tree/main/01_todo_app)
linked from the [FastHTML homepage](https://fastht.ml/).

## Full Example \#2 - Image Generation App

Let’s create an image generation app. We’d like to wrap a text-to-image
model in a nice UI, where the user can type in a prompt and see a
generated image appear. We’ll use a model hosted by
[Replicate](https://replicate.com) to actually generate the images.
Let’s start with the homepage, with a form to submit prompts and a div
to hold the generated images:

``` python
# Main page
@app.get("/")
def get():
    inp = Input(id="new-prompt", name="prompt", placeholder="Enter a prompt")
    add = Form(Group(inp, Button("Generate")), hx_post="/", target_id='gen-list', hx_swap="afterbegin")
    gen_list = Div(id='gen-list')
    return Title('Image Generation Demo'), Main(H1('Magic Image Generation'), add, gen_list, cls='container')
```

Submitting the form will trigger a POST request to `/`, so next we need
to generate an image and add it to the list. One problem: generating
images is slow! We’ll start the generation in a separate thread, but
this now surfaces a different problem: we want to update the UI right
away, but our image will only be ready a few seconds later. This is a
common pattern - think about how often you see a loading spinner online.
We need a way to return a temporary bit of UI which will eventually be
replaced by the final image. Here’s how we might do this:

``` python
def generation_preview(id):
    if os.path.exists(f"gens/{id}.png"):
        return Div(Img(src=f"/gens/{id}.png"), id=f'gen-{id}')
    else:
        return Div("Generating...", id=f'gen-{id}', 
                   hx_post=f"/generations/{id}",
                   hx_trigger='every 1s', hx_swap='outerHTML')
    
@app.post("/generations/{id}")
def get(id:int): return generation_preview(id)

@app.post("/")
def post(prompt:str):
    id = len(generations)
    generate_and_save(prompt, id)
    generations.append(prompt)
    clear_input =  Input(id="new-prompt", name="prompt", placeholder="Enter a prompt", hx_swap_oob='true')
    return generation_preview(id), clear_input

@threaded
def generate_and_save(prompt, id): ... 
```

The form sends the prompt to the `/` route, which starts the generation
in a separate thread then returns two things:

- A generation preview element that will be added to the top of the
  `gen-list` div (since that is the target_id of the form which
  triggered the request)
- An input field that will replace the form’s input field (that has the
  same id), using the hx_swap_oob=‘true’ trick. This clears the prompt
  field so the user can type another prompt.

The generation preview first returns a temporary “Generating…” message,
which polls the `/generations/{id}` route every second. This is done by
setting hx_post to the route and hx_trigger to ‘every 1s’. The
`/generations/{id}` route returns the preview element every second until
the image is ready, at which point it returns the final image. Since the
final image replaces the temporary one (hx_swap=‘outerHTML’), the
polling stops running and the generation preview is now complete.

This works nicely - the user can submit several prompts without having
to wait for the first one to generate, and as the images become
available they are added to the list. You can see the full code of this
version
[here](https://github.com/AnswerDotAI/fasthtml-example/blob/main/image_app_simple/draft1.py).

### Again, with Style

The app is functional, but can be improved. The [next
version](https://github.com/AnswerDotAI/fasthtml-example/blob/main/image_app_simple/main.py)
adds more stylish generation previews, lays out the images in a grid
layout that is responsive to different screen sizes, and adds a database
to track generations and make them persistent. The database part is very
similar to the todo list example, so let’s just quickly look at how we
add the nice grid layout. This is what the result looks like:

<figure>
<img src="by_example_files/figure-commonmark/cell-58-1-image.png"
alt="image.png" />
<figcaption aria-hidden="true">image.png</figcaption>
</figure>

Step one was looking around for existing components. The Pico CSS
library we’ve been using has a rudimentary grid but recommends using an
alternative layout system. One of the options listed was
[Flexbox](http://flexboxgrid.com/).

To use Flexbox you create a “row” with one or more elements. You can
specify how wide things should be with a specific syntax in the class
name. For example, `col-xs-12` means a box that will take up 12 columns
(out of 12 total) of the row on extra small screens, `col-sm-6` means a
column that will take up 6 columns of the row on small screens, and so
on. So if you want four columns on large screens you would use
`col-lg-3` for each item (i.e. each item is using 3 columns out of 12).

``` html
<div class="row">
    <div class="col-xs-12">
        <div class="box">This takes up the full width</div>
    </div>
</div>
```

This was non-intuitive to me. Thankfully ChatGPT et al know web stuff
quite well, and we can also experiment in a notebook to test things out:

``` python
grid = Html(
    Link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/flexboxgrid/6.3.1/flexboxgrid.min.css", type="text/css"),
    Div(
        Div(Div("This takes up the full width", cls="box", style="background-color: #800000;"), cls="col-xs-12"),
        Div(Div("This takes up half", cls="box", style="background-color: #008000;"), cls="col-xs-6"),
        Div(Div("This takes up half", cls="box", style="background-color: #0000B0;"), cls="col-xs-6"),
        cls="row", style="color: #fff;"
    )
)
show(grid)
```

<!doctype html></!doctype>
&#10;<html>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flexboxgrid/6.3.1/flexboxgrid.min.css" type="text/css">
  <div class="row" style="color: #fff;">
    <div class="col-xs-12">
      <div class="box" style="background-color: #800000;">This takes up the full width</div>
    </div>
    <div class="col-xs-6">
      <div class="box" style="background-color: #008000;">This takes up half</div>
    </div>
    <div class="col-xs-6">
      <div class="box" style="background-color: #0000B0;">This takes up half</div>
    </div>
  </div>
</html>

Aside: when in doubt with CSS stuff, add a background color or a border
so you can see what’s happening!

Translating this into our app, we have a new homepage with a
`div (class="row")` to store the generated images / previews, and a
`generation_preview` function that returns boxes with the appropriate
classes and styles to make them appear in the grid. I chose a layout
with different numbers of columns for different screen sizes, but you
could also *just* specify the `col-xs` class if you wanted the same
layout on all devices.

``` python
gridlink = Link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/flexboxgrid/6.3.1/flexboxgrid.min.css", type="text/css")
app = FastHTML(hdrs=(picolink, gridlink))

# Main page
@app.get("/")
def get():
    inp = Input(id="new-prompt", name="prompt", placeholder="Enter a prompt")
    add = Form(Group(inp, Button("Generate")), hx_post="/", target_id='gen-list', hx_swap="afterbegin")
    gen_containers = [generation_preview(g) for g in gens(limit=10)] # Start with last 10
    gen_list = Div(*gen_containers[::-1], id='gen-list', cls="row") # flexbox container: class = row
    return Title('Image Generation Demo'), Main(H1('Magic Image Generation'), add, gen_list, cls='container')

# Show the image (if available) and prompt for a generation
def generation_preview(g):
    grid_cls = "box col-xs-12 col-sm-6 col-md-4 col-lg-3"
    image_path = f"{g.folder}/{g.id}.png"
    if os.path.exists(image_path):
        return Div(Card(
                       Img(src=image_path, alt="Card image", cls="card-img-top"),
                       Div(P(B("Prompt: "), g.prompt, cls="card-text"),cls="card-body"),
                   ), id=f'gen-{g.id}', cls=grid_cls)
    return Div(f"Generating gen {g.id} with prompt {g.prompt}", 
            id=f'gen-{g.id}', hx_get=f"/gens/{g.id}", 
            hx_trigger="every 2s", hx_swap="outerHTML", cls=grid_cls)
```

You can see the final result in
[main.py](https://github.com/AnswerDotAI/fasthtml-example/blob/main/image_app_simple/main.py)
in the `image_app_simple` example directory, along with info on
deploying it (tl;dr don’t!). We’ve also deployed a version that only
shows *your* generations (tied to browser session) and has a credit
system to save our bank accounts. You can access that
[here](https://image-gen-public-credit-pool.replit.app/). Now for the
next question: how do we keep track of different users?

### Again, with Sessions

At the moment everyone sees all images! How do we keep some sort of
unique identifier tied to a user? Before going all the way to setting up
users, login pages etc., let’s look at a way to at least limit
generations to the user’s *session*. You could do this manually with
cookies. For convenience and security, fasthtml (via Starlette) has a
special mechanism for storing small amounts of data in the user’s
browser via the `session` argument to your route. This acts like a
dictionary and you can set and get values from it. For example, here we
look for a `session_id` key, and if it doesn’t exist we generate a new
one:

``` python
@app.get("/")
def get(session):
    if 'session_id' not in session: session['session_id'] = str(uuid.uuid4())
    return H1(f"Session ID: {session['session_id']}")
```

Refresh the page a few times - you’ll notice that the session ID remains
the same. If you clear your browsing data, you’ll get a new session ID.
And if you load the page in a different browser (but not a different
tab), you’ll get a new session ID. This will persist within the current
browser, letting us use it as a key for our generations. As a bonus,
someone can’t spoof this session id by passing it in another way (for
example, sending a query parameter). Behind the scenes, the data *is*
stored in a browser cookie but it is signed with a secret key that stops
the user or anyone nefarious from being able to tamper with it. The
cookie is decoded back into a dictionary by something called a
middleware function, which we won’t cover here. All you need to know is
that we can use this to store bits of state in the user’s browser.

In the image app example, we can add a `session_id` column to our
database, and modify our homepage like so:

``` python
@app.get("/")
def get(session):
    if 'session_id' not in session: session['session_id'] = str(uuid.uuid4())
    inp = Input(id="new-prompt", name="prompt", placeholder="Enter a prompt")
    add = Form(Group(inp, Button("Generate")), hx_post="/", target_id='gen-list', hx_swap="afterbegin")
    gen_containers = [generation_preview(g) for g in gens(limit=10, where=f"session_id == '{session['session_id']}'")]
    ...
```

So we check if the session id exists in the session, add one if not, and
then limit the generations shown to only those tied to this session id.
We filter the database with a where clause - see \[TODO link Jeremy’s
example for a more reliable way to do this\]. The only other change we
need to make is to store the session id in the database when a
generation is made. You can check out this version
[here](https://github.com/AnswerDotAI/fasthtml-example/blob/main/image_app_session_credits/session.py).
You could instead write this app without relying on a database at all -
simply storing the filenames of the generated images in the session, for
example. But this more general approach of linking some kind of unique
session identifier to users or data in our tables is a useful general
pattern for more complex examples.

### Again, with Credits!

Generating images with replicate costs money. So next let’s add a pool
of credits that get used up whenever anyone generates an image. To
recover our lost funds, we’ll also set up a payment system so that
generous users can buy more credits for everyone. You could modify this
to let users buy credits tied to their session ID, but at that point you
risk having angry customers losing their money after wiping their
browser history, and should consider setting up proper account
management :)

Taking payments with Stripe is intimidating but very doable. [Here’s a
tutorial](https://testdriven.io/blog/flask-stripe-tutorial/) that shows
the general principle using Flask. As with other popular tasks in the
web-dev world, ChatGPT knows a lot about Stripe - but you should
exercise extra caution when writing code that handles money!

For the [finished
example](https://github.com/AnswerDotAI/fasthtml-example/blob/main/image_app_session_credits/main.py)
we add the bare minimum:

- A way to create a Stripe checkout session and redirect the user to the
  session URL
- ‘Success’ and ‘Cancel’ routes to handle the result of the checkout
- A route that listens for a webhook from Stripe to update the number of
  credits when a payment is made.

In a typical application you’ll want to keep track of which users make
payments, catch other kinds of stripe events and so on. This example is
more a ‘this is possible, do your own research’ than ‘this is how you do
it’. But hopefully it does illustrate the key idea: there is no magic
here. Stripe (and many other technologies) relies on sending users to
different routes and shuttling data back and forth in requests. And we
know how to do that!

## More on Routing and Request Parameters

There are a number of ways information can be passed to the server. When
you specify arguments to a route, FastHTML will search the request for
values with the same name, and convert them to the correct type. In
order, it searches

- The path parameters
- The query parameters
- The cookies
- The headers
- The session
- Form data

There are also a few special arguments

- `request` (or any prefix like `req`): gets the raw Starlette `Request`
  object
- `session` (or any prefix like `sess`): gets the session object
- `auth`
- `htmx`
- `app`

In this section let’s quickly look at some of these in action.

``` python
from fasthtml.common import *
from starlette.testclient import TestClient

app = FastHTML()
cli = TestClient(app)
```

Part of the route (path parameters):

``` python
@app.get('/user/{nm}')
def _(nm:str): return f"Good day to you, {nm}!"

cli.get('/user/jph').text
```

    'Good day to you, jph!'

Matching with a regex:

``` python
reg_re_param("imgext", "ico|gif|jpg|jpeg|webm")

@app.get(r'/static/{path:path}/{fn}.{ext:imgext}')
def get_img(fn:str, path:str, ext:str): return f"Getting {fn}.{ext} from /{path}"

cli.get('/static/foo/jph.ico').text
```

    'Getting jph.ico from /foo/'

Using an enum (try using a string that isn’t in the enum):

``` python
ModelName = str_enum('ModelName', "alexnet", "resnet", "lenet")

@app.get("/models/{nm}")
def model(nm:ModelName): return nm

print(cli.get('/models/alexnet').text)
```

    alexnet

Casting to a Path:

``` python
@app.get("/files/{path}")
def txt(path: Path): return path.with_suffix('.txt')

print(cli.get('/files/foo').text)
```

    foo.txt

An integer with a default value:

``` python
fake_db = [{"name": "Foo"}, {"name": "Bar"}]

@app.get("/items/")
def read_item(idx: int = 0): return fake_db[idx]

print(cli.get('/items/?idx=1').text)
```

    {"name":"Bar"}

``` python
# Equivalent to `/items/?idx=0`.
print(cli.get('/items/').text)
```

    {"name":"Foo"}

Boolean values (takes anything “truthy” or “falsy”):

``` python
@app.get("/booly/")
def booly(coming:bool=True): return 'Coming' if coming else 'Not coming'

print(cli.get('/booly/?coming=true').text)
```

    Coming

``` python
print(cli.get('/booly/?coming=no').text)
```

    Not coming

Getting dates:

``` python
@app.get("/datie/")
def datie(d:parsed_date): return d

date_str = "17th of May, 2024, 2p"
print(cli.get(f'/datie/?d={date_str}').text)
```

    2024-05-17 14:00:00

Matching a dataclass:

``` python
from dataclasses import dataclass, asdict

@dataclass
class Bodie:
    a:int;b:str

@app.route("/bodie/{nm}")
def post(nm:str, data:Bodie):
    res = asdict(data)
    res['nm'] = nm
    return res

cli.post('/bodie/me', data=dict(a=1, b='foo')).text
```

    '{"a":1,"b":"foo","nm":"me"}'

### Cookies

Cookies can be set via a Starlette Response object, and can be read back
by specifying the name:

``` python
from datetime import datetime

@app.get("/setcookie")
def setc(req):
    now = datetime.now()
    res = Response(f'Set to {now}')
    res.set_cookie('now', str(now))
    return res

cli.get('/setcookie').text
```

    'Set to 2024-07-20 23:14:54.364793'

``` python
@app.get("/getcookie")
def getc(now:parsed_date): return f'Cookie was set at time {now.time()}'

cli.get('/getcookie').text
```

    'Cookie was set at time 23:14:54.364793'

### User Agent and HX-Request

An argument of `user_agent` will match the header `User-Agent`. This
holds for special headers like `HX-Request` (used by HTMX to signal when
a request comes from an HTMX request) - the general pattern is that “-”
is replaced with “\_” and strings are turned to lowercase.

``` python
@app.get("/ua")
async def ua(user_agent:str): return user_agent

cli.get('/ua', headers={'User-Agent':'FastHTML'}).text
```

    'FastHTML'

``` python
@app.get("/hxtest")
def hxtest(htmx): return htmx.request

cli.get('/hxtest', headers={'HX-Request':'1'}).text
```

    '1'

### Starlette Requests

If you add an argument called `request`(or any prefix of that, for
example `req`) it will be populated with the Starlette `Request` object.
This is useful if you want to do your own processing manually. For
example, although FastHTML will parse forms for you, you could instead
get form data like so:

``` python
@app.get("/form")
async def form(request:Request):
    form_data = await request.form()
    a = form_data.get('a')
```

See the [Starlette docs](https://starlette.io/docs/) for more
information on the `Request` object.

### Starlette Responses

You can return a Starlette Response object from a route to control the
response. For example:

``` python
@app.get("/redirect")
def redirect():
    return RedirectResponse(url="/")
```

We used this to set cookies in the previous example. See the [Starlette
docs](https://starlette.io/docs/) for more information on the `Response`
object.

### Static Files

We often want to serve static files like images. This is easily done!
For common file types (images, CSS etc) we can create a route that
returns a Starlette `FileResponse` like so:

``` python
# For images, CSS, etc.
@app.get("/{fname:path}.{ext:static}")
def static(fname: str, ext: str):
  return FileResponse(f'{fname}.{ext}')
```

You can customize it to suit your needs (for example, only serving files
in a certain directory). You’ll notice some variant of this route in all
our complete examples - even for apps with no static files the browser
will typically request a `/favicon.ico` file, for example, and as the
astute among you will have noticed this has sparked a bit of competition
between Johno and Jeremy regarding which country flag should serve as
the default!

### WebSockets

For certain applications such as multiplayer games, websockets can be a
powerful feature. Luckily HTMX and FastHTML has you covered! Simply
specify that you wish to include the websocket header extension from
HTMX:

``` python
app = FastHTML(exts='ws')
rt = app.route
```

With that, you are now able to specify the different websocket specific
HTMX goodies. For example, say we have a website we want to setup a
websocket, you can simply:

``` python
def mk_inp(): return Input(id='msg')

@rt('/')
async def get(request):
    cts = Div(
        Div(id='notifications'),
        Form(mk_inp(), id='form', ws_send=True),
        hx_ext='ws', ws_connect='/ws')
    return Titled('Websocket Test', cts)
```

And this will setup a connection on the route `/ws` along with a form
that will send a message to the websocket whenever the form is
submitted. Let’s go ahead and handle this route:

``` python
@app.ws('/ws')
async def ws(msg:str, send):
    await send(Div('Hello ' + msg, id="notifications"))
    await sleep(2)
    return Div('Goodbye ' + msg, id="notifications"), mk_inp()
```

One thing you might have noticed is a lack of target id for our
websocket trigger for swapping HTML content. This is because HTMX always
swaps content with websockets with Out of Band Swaps. Therefore, HTMX
will look for the id in the returned HTML content from the server for
determining what to swap. To send stuff to the client, you can either
use the `send` parameter or simply return the content or both!

Now, sometimes you might want to perform actions when a client connects
or disconnects such as add or remove a user from a player queue. To hook
into these events, you can pass your connection or disconnection
function to the `app.ws` decorator:

``` python
async def on_connect(send):
    print('Connected!')
    await send(Div('Hello, you have connected', id="notifications"))

async def on_disconnect(ws):
    print('Disconnected!')

@app.ws('/ws', conn=on_connect, disconn=on_disconnect)
async def ws(msg:str, send):
    await send(Div('Hello ' + msg, id="notifications"))
    await sleep(2)
    return Div('Goodbye ' + msg, id="notifications"), mk_inp()
```

## Full Example \#3 - Chatbot Example with DaisyUI Components

Let’s go back to the topic of adding components or styling beyond the
simple PicoCSS examples so far. How might we adopt a component or
framework? In this example, let’s build a chatbot UI leveraging the
[DaisyUI chat bubble](https://daisyui.com/components/chat/). The final
result will look like this:

<figure>
<img src="by_example_files/figure-commonmark/cell-101-1-image.png"
alt="image.png" />
<figcaption aria-hidden="true">image.png</figcaption>
</figure>

At first glance, DaisyUI’s chat component looks quite intimidating. The
examples look like this:

``` html
<div class="chat chat-start">
  <div class="chat-image avatar">
    <div class="w-10 rounded-full">
      <img alt="Tailwind CSS chat bubble component" src="https://img.daisyui.com/images/stock/photo-1534528741775-53994a69daeb.jpg" />
    </div>
  </div>
  <div class="chat-header">
    Obi-Wan Kenobi
    <time class="text-xs opacity-50">12:45</time>
  </div>
  <div class="chat-bubble">You were the Chosen One!</div>
  <div class="chat-footer opacity-50">
    Delivered
  </div>
</div>
<div class="chat chat-end">
  <div class="chat-image avatar">
    <div class="w-10 rounded-full">
      <img alt="Tailwind CSS chat bubble component" src="https://img.daisyui.com/images/stock/photo-1534528741775-53994a69daeb.jpg" />
    </div>
  </div>
  <div class="chat-header">
    Anakin
    <time class="text-xs opacity-50">12:46</time>
  </div>
  <div class="chat-bubble">I hate you!</div>
  <div class="chat-footer opacity-50">
    Seen at 12:46
  </div>
</div>
```

We have several things going for us however.

- ChatGPT knows DaisyUI and Tailwind (DaisyUI is a Tailwind component
  library)
- We can build things up piece by piece with AI standing by to help.

<https://h2f.answer.ai/> is a tool that can convert HTML to FT
(fastcore.xml) and back, which is useful for getting a quick starting
point when you have an HTML example to start from.

We can strip out some unnecessary bits and try to get the simplest
possible example working in a notebook first:

``` python
# Loading tailwind and daisyui
headers = (Script(src="https://cdn.tailwindcss.com"),
           Link(rel="stylesheet", href="https://cdn.jsdelivr.net/npm/daisyui@4.11.1/dist/full.min.css"))

# Displaying a single message
d = Div(
    Div("Chat header here", cls="chat-header"),
    Div("My message goes here", cls="chat-bubble chat-bubble-primary"),
    cls="chat chat-start"
)
# show(Html(*headers, d)) # uncomment to view
```

Now we can extend this to render multiple messages, with the message
being on the left (`chat-start`) or right (`chat-end`) depending on the
role. While we’re at it, we can also change the color
(`chat-bubble-primary`) of the message and put them all in a `chat-box`
div:

``` python
messages = [
    {"role":"user", "content":"Hello"},
    {"role":"assistant", "content":"Hi, how can I assist you?"}
]

def ChatMessage(msg):
    return Div(
        Div(msg['role'], cls="chat-header"),
        Div(msg['content'], cls=f"chat-bubble chat-bubble-{'primary' if msg['role'] == 'user' else 'secondary'}"),
        cls=f"chat chat-{'end' if msg['role'] == 'user' else 'start'}")

chatbox = Div(*[ChatMessage(msg) for msg in messages], cls="chat-box", id="chatlist")

# show(Html(*headers, chatbox)) # Uncomment to view
```

Next, it was back to the ChatGPT to tweak the chat box so it wouldn’t
grow as messages were added. I asked:

    "I have something like this (it's working now) 
    [code]
    The messages are added to this div so it grows over time. 
    Is there a way I can set it's height to always be 80% of the total window height with a scroll bar if needed?"

Based on this query GPT4o helpfully shared that “This can be achieved
using Tailwind CSS utility classes. Specifically, you can use h-\[80vh\]
to set the height to 80% of the viewport height, and overflow-y-auto to
add a vertical scroll bar when needed.”

To put it another way: none of the CSS classes in the following example
were written by a human, and what edits I did make were informed by
advice from the AI that made it relatively painless!

The actual chat functionality of the app is based on our
[claudette](https://claudette.answer.ai/) library. As with the image
example, we face a potential hiccup in that getting a response from an
LLM is slow. We need a way to have the user message added to the UI
immediately, and then have the response added once it’s available. We
could do something similar to the image generation example above, or use
websockets. Check out the [full
example](https://github.com/AnswerDotAI/fasthtml-example/tree/main/02_chatbot)
for implementations of both, along with further details.

## Full Example \#4 - Multiplayer Game of Life Example with Websockets

Let’s see how we can implement a collaborative website using Websockets
in FastHTML. To showcase this, we will use the famous [Conway’s Game of
Life](https://en.wikipedia.org/wiki/Conway's_Game_of_Life), which is a
game that takes place in a grid world. Each cell in the grid can be
either alive or dead. The cell’s state is initially given by a user
before the game is started and then evolves through the iteration of the
grid world once the clock starts. Whether a cell’s state will change
from the previous state depends on simple rules based on its neighboring
cells’ states. Here is the standard Game of Life logic implemented in
Python courtesy of ChatGPT:

``` python
grid = [[0 for _ in range(20)] for _ in range(20)]
def update_grid(grid: list[list[int]]) -> list[list[int]]:
    new_grid = [[0 for _ in range(20)] for _ in range(20)]
    def count_neighbors(x, y):
        directions = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
        count = 0
        for dx, dy in directions:
            nx, ny = x + dx, y + dy
            if 0 <= nx < len(grid) and 0 <= ny < len(grid[0]): count += grid[nx][ny]
        return count
    for i in range(len(grid)):
        for j in range(len(grid[0])):
            neighbors = count_neighbors(i, j)
            if grid[i][j] == 1:
                if neighbors < 2 or neighbors > 3: new_grid[i][j] = 0
                else: new_grid[i][j] = 1
            elif neighbors == 3: new_grid[i][j] = 1
    return new_grid
```

This would be a very dull game if we were to run it, since the initial
state of everything would remain dead. Therefore, we need a way of
letting the user give an initial state before starting the game.
FastHTML to the rescue!

``` python
def Grid():
    cells = []
    for y, row in enumerate(game_state['grid']):
        for x, cell in enumerate(row):
            cell_class = 'alive' if cell else 'dead'
            cell = Div(cls=f'cell {cell_class}', hx_put='/update', hx_vals={'x': x, 'y': y}, hx_swap='none', hx_target='#gol', hx_trigger='click')
            cells.append(cell)
    return Div(*cells, id='grid')

@rt('/update')
async def put(x: int, y: int):
    grid[y][x] = 1 if grid[y][x] == 0 else 0
```

Above is a component for representing the game’s state that the user can
interact with and update on the server using cool HTMX features such as
`hx_vals` for determining which cell was clicked to make it dead or
alive. Now, you probably noticed that the HTTP request in this case is a
PUT request, which does not return anything and this means our client’s
view of the grid world and the server’s game state will immediately
become out of sync :(. We could of course just return a new Grid
component with the updated state, but that would only work for a single
client, if we had more, they quickly get out of sync with each other and
the server. Now Websockets to the rescue!

Websockets are a way for the server to keep a persistent connection with
clients and send data to the client without explicitly being requested
for information, which is not possible with HTTP. Luckily FastHTML and
HTMX work well with Websockets. Simply state you wish to use websockets
for your app and define a websocket route:

``` python
...
app = FastHTML(hdrs=(picolink, gridlink, css, htmx_ws), exts='ws')

player_queue = []
async def update_players():
    for i, player in enumerate(player_queue):
        try: await player(Grid())
        except: player_queue.pop(i)
async def on_connect(send): player_queue.append(send)
async def on_disconnect(send): await update_players()

@app.ws('/gol', conn=on_connect, disconn=on_disconnect)
async def ws(msg:str, send): pass

def Home(): return Title('Game of Life'), Main(gol, Div(Grid(), id='gol', cls='row center-xs'), hx_ext="ws", ws_connect="/gol")

@rt('/update')
async def put(x: int, y: int):
    grid[y][x] = 1 if grid[y][x] == 0 else 0
    await update_players()
...
```

Here we simply keep track of all the players that have connected or
disconnected to our site and when an update occurs, we send updates to
all the players still connected via websockets. Via HTMX, you are still
simply exchanging HTML from the server to the client and will swap in
the content based on how you setup your `hx_swap` attribute. There is
only one difference, that being all swaps are OOB. You can find more
information on the HTMX websocket extension documentation page
[here](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/ws/README.md).
You can find a full fledge hosted example of this app
[here](https://game-of-life-production-ed7f.up.railway.app/).

## FT objects and HTML

These FT objects create a ‘FastTag’ structure \[tag,children,attrs\] for
`to_xml()`. When we call `Div(...)`, the elements we pass in are the
children. Attributes are passed in as keywords. `class` and `for` are
special words in python, so we use `cls`, `klass` or `_class` instead of
`class` and `fr` or `_for` instead of `for`. Note these objects are just
3-element lists - you can create custom ones too as long as they’re also
3-element lists. Alternately, leaf nodes can be strings instead (which
is why you can do `Div('some text')`). If you pass something that isn’t
a 3-element list or a string, it will be converted to a string using
str()… unless (our final trick) you define a `__ft__` method that will
run before str(), so you can render things a custom way.

For example, here’s one way we could make a custom class that can be
rendered into HTML:

``` python
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __ft__(self):
        return ['div', [f'{self.name} is {self.age} years old.'], {}]

p = Person('Jonathan', 28)
print(to_xml(Div(p, "more text", cls="container")))
```

    <div class="container">
      <div>Jonathan is 28 years old.</div>
    more text
    </div>

In the examples, you’ll see we often patch in `__ft__` methods to
existing classes to control how they’re rendered. For example, if Person
didn’t have a `__ft__` method or we wanted to override it, we could add
a new one like this:

``` python
from fastcore.all import patch

@patch
def __ft__(self:Person):
    return Div("Person info:", Ul(Li("Name:",self.name), Li("Age:", self.age)))

show(p)
```

<div>
Person info:
  <ul>
    <li>
Name:
Jonathan
    </li>
    <li>
Age:
28
    </li>
  </ul>
</div>

Some tags from fastcore.xml are overwritten by fasthtml.core and a few
are further extended by fasthtml.xtend using this method. Over time, we
hope to see others developing custom components too, giving us a larger
and larger ecosystem of reusable components.

## Custom Scripts and Styling

There are many popular JavaScript and CSS libraries that can be used via
a simple [`Script`](https://www.fastht.ml/docs/api/xtend.html#script) or
[`Style`](https://www.fastht.ml/docs/api/xtend.html#style) tag. But in
some cases you will need to write more custom code. FastHTML’s
[js.py](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/js.py)
contains a few examples that may be useful as reference.

For example, to use the [marked.js](https://marked.js.org/) library to
render markdown in a div, including in components added after the page
has loaded via htmx, we do something like this:

``` javascript
import { marked } from "https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js";
proc_htmx('%s', e => e.innerHTML = marked.parse(e.textContent));
```

`proc_htmx` is a shortcut that we wrote to apply a function to elements
matching a selector, including the element that triggered the event.
Here’s the code for reference:

``` javascript
export function proc_htmx(sel, func) {
  htmx.onLoad(elt => {
    const elements = htmx.findAll(elt, sel);
    if (elt.matches(sel)) elements.unshift(elt)
    elements.forEach(func);
  });
}
```

The [AI Pictionary
example](https://github.com/AnswerDotAI/fasthtml-example/tree/main/03_pictionary)
uses a larger chunk of custom JavaScript to handle the drawing canvas.
It’s a good example of the type of application where running code on the
client side makes the most sense, but still shows how you can integrate
it with FastHTML on the server side to add functionality (like the AI
responses) easily.

Adding styling with custom CSS and libraries such as tailwind is done
the same way we add custom JavaScript. The [doodle
example](https://github.com/AnswerDotAI/fasthtml-example/tree/main/doodle)
uses [Doodle.CSS](https://github.com/chr15m/DoodleCSS) to style the page
in a quirky way.

## Deploying Your App

We can deploy FastHTML almost anywhere you can deploy python apps. We’ve
tested Railway, Replit,
[HuggingFace](https://github.com/AnswerDotAI/fasthtml-hf), and
[PythonAnywhere](https://github.com/AnswerDotAI/fasthtml-example/blob/main/deploying-to-pythonanywhere.md).

### Railway

1.  [Install the Railway CLI](https://docs.railway.app/guides/cli) and
    sign up for an account.
2.  Set up a folder with our app as `main.py`
3.  In the folder, run `railway login`.
4.  Use the `fh_railway_deploy` script to deploy our project:

``` bash
fh_railway_deploy MY_APP_NAME
```

What the script does for us:

4.  Do we have an existing railway project?
    - Yes: Link the project folder to our existing Railway project.
    - No: Create a new Railway project.
5.  Deploy the project. We’ll see the logs as the service is built and
    run!
6.  Fetches and displays the URL of our app.
7.  By default, mounts a `/app/data` folder on the cloud to our app’s
    root folder. The app is run in `/app` by default, so from our app
    anything we store in `/data` will persist across restarts.

A final note about Railway: We can add secrets like API keys that can be
accessed as environment variables from our apps via
[‘Variables’](https://docs.railway.app/guides/variables). For example,
for the [image generation
app](https://github.com/AnswerDotAI/fasthtml-example/tree/main/image_app_simple),
we can add a `REPLICATE_API_KEY` variable, and then in `main.py` we can
access it as `os.environ['REPLICATE_API_KEY']`.

### Replit

Fork [this repl](https://replit.com/@johnowhitaker/FastHTML-Example) for
a minimal example you can edit to your heart’s content. `.replit` has
been edited to add the right run command
(`run = ["uvicorn", "main:app", "--reload"]`) and to set up the ports
correctly. FastHTML was installed with `poetry add python-fasthtml`, you
can add additional packages as needed in the same way. Running the app
in Replit will show you a webview, but you may need to open in a new tab
for all features (such as cookies) to work. When you’re ready, you can
deploy your app by clicking the ‘Deploy’ button. You pay for usage - for
an app that is mostly idle the cost is usually a few cents per month.

You can store secrets like API keys via the ‘Secrets’ tab in the Replit
project settings.

### HuggingFace

Follow the instructions in [this
repository](https://github.com/AnswerDotAI/fasthtml-hf) to deploy to
HuggingFace spaces.

## Where Next?

We’ve covered a lot of ground here! Hopefully this has given you plenty
to work with in building your own FastHTML apps. If you have any
questions, feel free to ask in the \#fasthtml Discord channel (in the
fastai Discord community). You can look through the other examples in
the [fasthtml-example
repository](https://github.com/AnswerDotAI/fasthtml-example) for more
ideas, and keep an eye on Jeremy’s [YouTube
channel](https://www.youtube.com/@howardjeremyp) where we’ll be
releasing a number of “dev chats” related to FastHTML in the near
future.</doc><doc title="Using Jupyter to write FastHTML" desc="A guide to developing FastHTML apps inside Jupyter notebooks."># Using Jupyter to write FastHTML



Writing FastHTML applications in Jupyter notebooks requires a slightly
different process than normal Python applications.

<div>

> **Download this notebook and try it yourself**
>
> The source code for this page is a [Jupyter
> notebook](https://github.com/AnswerDotAI/fasthtml/blob/main/nbs/tutorials/jupyter_and_fasthtml.ipynb).
> That makes it easy to directly experiment with it. However, as this is
> working code that means we have to comment out a few things in order
> for the documentation to build.

</div>

The first step is to import necessary libraries. As using FastHTML
inside a Jupyter notebook is a special case, it remains a special
import.

``` python
from fasthtml.common import *
from fasthtml.jupyter import JupyUvi, HTMX
```

Let’s create an app with `fast_app`.

``` python
app, rt = fast_app(pico=True)
```

Define a route to test the application.

``` python
@rt
def index():
    return Titled('Hello, Jupyter',
           P('Welcome to the FastHTML + Jupyter example'),
           Button('Click', hx_get='/click', hx_target='#dest'),
           Div(id='dest')
    )
```

Create a `server` object using
[`JupyUvi`](https://www.fastht.ml/docs/api/jupyter.html#jupyuvi), which
also starts Uvicorn. The `server` runs in a separate thread from
Jupyter, so it can use normal HTTP client functions in a notebook.

``` python
server = JupyUvi(app)
```

<script>
document.body.addEventListener('htmx:configRequest', (event) => {
    if(event.detail.path.includes('://')) return;
    htmx.config.selfRequestsOnly=false;
    event.detail.path = `${location.protocol}//${location.hostname}:8000${event.detail.path}`;
});
</script>

The [`HTMX`](https://www.fastht.ml/docs/api/jupyter.html#htmx) callable
displays the server’s HTMX application in an iframe which can be
displayed by Jupyter notebook. Pass in the same `port` variable used in
the [`JupyUvi`](https://www.fastht.ml/docs/api/jupyter.html#jupyuvi)
callable above or leave it blank to use the default (8000).

``` python
# This doesn't display in the docs - uncomment and run it to see it in action
# HTMX()
```

We didn’t define the `/click` route, but that’s fine - we can define (or
change) it any time, and it’s dynamically inserted into the running app.
No need to restart or reload anything!

``` python
@rt
def click(): return P('You clicked me!')
```

## Full screen view

You can view your app outside of Jupyter by going to `localhost:PORT`,
where `PORT` is usually the default 8000, so in most cases just click
[this link](localhost:8000/).

## Graceful shutdowns

Use the `server.stop()` function displayed below. If you restart Jupyter
without calling this line the thread may not be released and the
[`HTMX`](https://www.fastht.ml/docs/api/jupyter.html#htmx) callable
above may throw errors. If that happens, a quick temporary fix is to
specify a different port number in JupyUvi and HTMX with the `port`
parameter.

Cleaner solutions to the dangling thread are to kill the dangling thread
(dependant on each operating system) or restart the computer.

``` python
server.stop()
```</doc><doc title="FT Components" desc="Explanation of the `FT` components, which are a way to write HTML in a Pythonic way."># **FT** Components



**FT**, or ‘FastTags’, are the display components of FastHTML. In fact,
the word “components” in the context of FastHTML is often synonymous
with **FT**.

For example, when we look at a FastHTML app, in particular the views, as
well as various functions and other objects, we see something like the
code snippet below. It’s the `return` statement that we want to pay
attention to:

``` python
from fasthtml.common import *

def example():
    # The code below is a set of ft components
    return Div(
            H1("FastHTML APP"),
            P("Let's do this"),
            cls="go"
    )
```

Let’s go ahead and call our function and print the result:

``` python
example()
```

``` xml
<div class="go">
  <h1>FastHTML APP</h1>
  <p>Let&#x27;s do this</p>
</div>
```

As you can see, when returned to the user from a Python callable, like a
function, the ft components are transformed into their string
representations of XML or XML-like content such as HTML. More concisely,
*ft turns Python objects into HTML*.

Now that we know what ft components look and behave like we can begin to
understand them. At their most fundamental level, ft components:

1.  Are Python callables, specifically functions, classes, methods of
    classes, lambda functions, and anything else called with parenthesis
    that returns a value.
2.  Return a sequence of values which has three elements:
    1.  The tag to be generated
    2.  The content of the tag, which is a tuple of strings/tuples. If a
        tuple, it is the three-element structure of an ft component
    3.  A dictionary of XML attributes and their values
3.  FastHTML’s default ft components words begin with an uppercase
    letter. Examples include `Title()`, `Ul()`, and `Div()` Custom
    components have included things like `BlogPost` and `CityMap`.

## How FastHTML names ft components

When it comes to naming ft components, FastHTML appears to break from
PEP8. Specifically, PEP8 specifies that when naming variables, functions
and instantiated classes we use the `snake_case_pattern`. That is to
say, lowercase with words separated by underscores. However, FastHTML
uses `PascalCase` for ft components.

There’s a couple of reasons for this:

1.  ft components can be made from any callable type, so adhering to any
    one pattern doesn’t make much sense
2.  It makes for easier reading of FastHTML code, as anything that is
    PascalCase is probably an ft component

## Default **FT** components

FastHTML has over 150 **FT** components designed to accelerate web
development. Most of these mirror HTML tags such as `<div>`, `<p>`,
`<a>`, `<title>`, and more. However, there are some extra tags added,
including:

- [`Titled`](https://www.fastht.ml/docs/api/xtend.html#titled), a
  combination of the `Title()` and `H1()` tags
- [`Socials`](https://www.fastht.ml/docs/api/xtend.html#socials),
  renders popular social media tags

## The `fasthtml.ft` Namespace

Some people prefer to write code using namespaces while adhering to
PEP8. If that’s a preference, projects can be coded using the
`fasthtml.ft` namespace.

``` python
from fasthtml import ft

ft.Ul(
    ft.Li("one"),
    ft.Li("two"),
    ft.Li("three")
)
```

``` xml
<ul>
  <li>one</li>
  <li>two</li>
  <li>three</li>
</ul>
```

## Attributes

This example demonstrates many important things to know about how ft
components handle attributes.

``` python
#| echo: False
Label(
    "Choose an option", 
    Select(
        Option("one", value="1", selected=True),
        Option("two", value="2", selected=False),
        Option("three", value=3),
        cls="selector",
        _id="counter",
        **{'@click':"alert('Clicked');"},
    ),
    _for="counter",
)
```

Line 2  
Line 2 demonstrates that FastHTML appreciates `Label`s surrounding their
fields.

Line 5  
On line 5, we can see that attributes set to the `boolean` value of
`True` are rendered with just the name of the attribute.

Line 6  
On line 6, we demonstrate that attributes set to the `boolean` value of
`False` do not appear in the rendered output.

Line 7  
Line 7 is an example of how integers and other non-string values in the
rendered output are converted to strings.

Line 8  
Line 8 is where we set the HTML class using the `cls` argument. We use
`cls` here as `class` is a reserved word in Python. During the rendering
process this will be converted to the word “class”.

Line 9  
Line 9 demonstrates that any named argument passed into an ft component
will have the leading underscore stripped away before rendering. Useful
for handling reserved words in Python.

Line 10  
On line 10 we have an attribute name that cannot be represented as a
python variable. In cases like these, we can use an unpacked `dict` to
represent these values.

Line 12  
The use of `_for` on line 12 is another demonstration of an argument
having the leading underscore stripped during render. We can also use
`fr` as that will be expanded to `for`.

This renders the following HTML snippet:

``` python
Label(
    "Choose an option", 
    Select(
        Option("one", value="1", selected=True),
        Option("two", value="2", selected=False),
        Option("three", value=3),  # <4>,
        cls="selector",
        _id="counter",
        **{'@click':"alert('Clicked');"},
    ),
    _for="counter",
)
```

``` xml
<label for="counter">
Choose an option
  <select id="counter" @click="alert(&#x27;Clicked&#x27;);" class="selector" name="counter">
    <option value="1" selected>one</option>
    <option value="2" >two</option>
    <option value="3">three</option>
  </select>
</label>
```

## Defining new ft components

It is possible and sometimes useful to create your own ft components
that generate non-standard tags that are not in the FastHTML library.
FastHTML supports created and defining those new tags flexibly.

For more information, see the [Defining new ft
components](../ref/defining_xt_component.html) reference page.

## FT components and type hints

If you use type hints, we strongly suggest that FT components be treated
as the `Any` type.

The reason is that FastHTML leverages python’s dynamic features to a
great degree. Especially when it comes to `FT` components, which can
evaluate out to be `FT|str|None|tuple` as well as anything that supports
the `__ft__`, `__html__`, and `__str__` method. That’s enough of the
Python stack that assigning anything but `Any` to be the FT type will
prove an exercise in frustation.</doc><doc title="FAQ" desc="Answers to common questions about FastHTML."># FAQ



## Why does my editor say that I have errors in my FastHTML code?

Many editors, including Visual Studio Code, use PyLance to provide error
checking for Python. However, PyLance’s error checking is just a guess –
it can’t actually know whether your code is correct or not. PyLance
particularly struggles with FastHTML’s syntax, which leads to it often
reporting false error messages in FastHTML projects.

To avoid these misleading error messages, it’s best to disable some
PyLance error checking in your FastHTML projects. Here’s how to do it in
Visual Studio Code (the same approach should also work in other editors
based on vscode, such as Cursor and GitHub Codespaces):

1.  Open your FastHTML project
2.  Press `Ctrl+Shift+P` (or `Cmd+Shift+P` on Mac) to open the Command
    Palette
3.  Type “Preferences: Open Workspace Settings (JSON)” and select it
4.  In the JSON file that opens, add the following lines:

``` json
{
 "python.analysis.diagnosticSeverityOverrides": {
      "reportGeneralTypeIssues": "none",
      "reportOptionalMemberAccess": "none",
      "reportWildcardImportFromLibrary": "none",
      "reportRedeclaration": "none",
      "reportAttributeAccessIssue": "none",
      "reportInvalidTypeForm": "none",
      "reportAssignmentType": "none",
  }
}
```

5.  Save the file

Even with PyLance diagnostics turned off, your FastHTML code will still
run correctly. If you’re still seeing some false errors from PyLance,
you can disable it entirely by adding this to your settings:

``` json
{
  "python.analysis.ignore": [  "*"  ]
}
```

## Why the distinctive coding style?

FastHTML coding style is the [fastai coding
style](https://docs.fast.ai/dev/style.html).

If you are coming from a data science background the **fastai coding
style** may already be your preferred style.

If you are coming from a PEP-8 background where the use of ruff is
encouraged, there is a learning curve. However, once you get used to the
**fastai coding style** you may discover yourself appreciating the
concise nature of this style. It also encourages using more functional
programming tooling, which is both productive and fun. Having said that,
it’s entirely optional!

## Why not JSX?

Many have asked! We think there’s no benefit… Python’s positional and kw
args precisely 1:1 map already to html/xml children and attrs, so
there’s no need for a new syntax.

We wrote some more thoughts on Why Python HTML components over Jinja2,
Mako, or JSX
[here](https://www.answer.ai/posts/2024-08-03-fasthtml.html#why).

## Why use `import *`

First, through the use of the
[`__all__`](https://docs.python.org/3/tutorial/modules.html#importing-from-a-package)
attribute in our Python modules we control what actually gets imported.
So there’s no risk of namespace pollution.

Second, our style lends itself to working in rather compact Jupyter
notebooks and small Python modules. Hence we know about the source code
whose libraries we `import *` from. This terseness means we can develop
faster. We’re a small team, and any edge we can gain is important to us.

Third, for external libraries, be it core Python, SQLAlchemy, or other
things we do tend to use explicit imports. In part to avoid namespace
collisions, and also as reference to know where things are coming from.

We’ll finish by saying a lot of our users employ explicit imports. If
that’s the path you want to take, we encourage the use of
`from fasthtml import common as fh`. The acronym of `fh` makes it easy
to recognize that a symbol is from the FastHTML library.

## Can FastHTML be used for dashboards?

Yes it can. In fact, it excels at building dashboards. In addition to
being great for building static dashboards, because of its
[foundation](https://fastht.ml/about/foundation) in ASGI and [tech
stack](https://fastht.ml/about/tech), FastHTML natively supports
Websockets. That means using FastHTML we can create dashboards that
autoupdate.

## Why is FastHTML developed using notebooks?

Some people are under the impression that writing software in notebooks
is bad.

[Watch this
video](https://www.youtube.com/watch?v=9Q6sLbz37gk&ab_channel=JeremyHoward).
We’ve used Jupyter notebooks exported via `nbdev` to write a wide range
of “very serious” software projects over the last three years. This
includes deep learning libraries, API clients, Python language
extensions, terminal user interfaces, web frameworks, and more!

[nbdev](https://nbdev.fast.ai/) is a Jupyter-powered tool for writing
software. Traditional programming environments throw away the result of
your exploration in REPLs or notebooks. `nbdev` makes exploration an
integral part of your workflow, all while promoting software engineering
best practices.

## Why not pyproject.toml for packaging?

FastHTML uses a `setup.py` module instead of a `pyproject.toml` file to
configure itself for installation. The reason for this is
`pyproject.toml` is not compatible with [nbdev](https://nbdev.fast.ai/),
which is what is used to write and build FastHTML.

The nbdev project spent around a year trying to move to pyproject.toml
but there was insufficient functionality in the toml-based approach to
complete the transition.</doc><doc title="MiniDataAPI Spec" desc="Explanation of the MiniDataAPI specification, which allows us to use the same API for many different database engines."># MiniDataAPI Spec



The `MiniDataAPI` is a persistence API specification that designed to be
small and relatively easy to implement across a wide range of
datastores. While early implementations have been SQL-based, the
specification can be quickly implemented in key/value stores, document
databases, and more.

<div>

> **Work in Progress**
>
> The MiniData API spec is a work in progress, subject to change. While
> the majority of design is complete, expect there could be breaking
> changes.

</div>

## Why?

The MiniDataAPI specification allows us to use the same API for many
different database engines. Any application using the MiniDataAPI spec
for interacting with its database requires no modification beyond import
and configuration changes to switch database engines. For example, to
convert an application from Fastlite running SQLite to FastSQL running
PostgreSQL, should require only changing these two lines:

<div class="columns">

<div class="column" width="19%"
style="border-right: 1px solid #ccc; padding-right: 10px;">

FastLite version

``` python
from fastlite import *
db = database('test.db')
```

</div>

<div class="column" width="79%" style="padding-left: 10px;">

FastSQL version

``` python
from fastsql import *
db = Database('postgres:...')
```

</div>

</div>

As both libraries adhere to the MiniDataAPI specification, the rest of
the code in the application should remain the same. The advantage of the
MiniDataAPI spec is that it allows people to use whatever datastores
they have access to or prefer.

<div>

> **Note**
>
> Switching databases won’t migrate any existing data between databases.

</div>

### Easy to learn, quick to implement

The MiniDataAPI specification is designed to be easy-to-learn and quick
to implement. It focuses on straightforward Create, Read, Update, and
Delete (CRUD) operations.

MiniDataAPI databases aren’t limited to just row-based systems. In fact,
the specification is closer in design to a key/value store than a set of
records. What’s exciting about this is we can write implementations for
tools like Python dict stored as JSON, Redis, and even the venerable
ZODB.

### Limitations of the MiniDataAPI Specification

> “Mini refers to the lightweightness of specification, not the data.”
>
> – Jeremy Howard

The advantages of the MiniDataAPI come at a cost. The MiniDataAPI
specification focuses a very small set of features compared to what can
be found in full-fledged ORMs and query languages. It intentionally
avoids nuances or sophisticated features.

This means the specification does not include joins or formal foreign
keys. Complex data stored over multiple tables that require joins isn’t
handled well. For this kind of scenario it’s probably for the best to
use more sophisticated ORMs or even direct database queries.

### Summary of the MiniDataAPI Design

- Easy-to-learn
- Relative quick to implement for new database engines
- An API for CRUD operations
- For many different types of databases including row- and
  key/value-based designs
- Intentionally small in terms of features: no joins, no foreign keys,
  no database specific features
- Best for simpler designs, complex architectures will need more
  sophisticated tools.

## Connect/construct the database

We connect or construct the database by passing in a string connecting
to the database endpoint or a filepath representing the database’s
location. While this example is for SQLite running in memory, other
databases such as PostgreSQL, Redis, MongoDB, might instead use a URI
pointing at the database’s filepath or endpoint. The method of
connecting to a DB is *not* part of this API, but part of the underlying
library. For instance, for fastlite:

``` python
db = database(':memory:')
```

Here’s a complete list of the available methods in the API, all
documented below (assuming `db` is a database and `t` is a table):

- `db.create`
- `t.insert`
- `t.delete`
- `t.update`
- `t[key]`
- `t(...)`
- `t.xtra`

## Tables

For the sake of expediency, this document uses a SQL example. However,
tables can represent anything, not just the fundamental construct of a
SQL databases. They might represent keys within a key/value structure or
files on a hard-drive.

### Creating tables

We use a `create()` method attached to `Database` object (`db` in our
example) to create the tables.

``` python
class User: name:str; email: str; year_started:int
users = db.create(User, pk='name')
users
```

    <Table user (name, email, year_started)>

``` python
class User: name:str; email: str; year_started:int
users = db.create(User, pk='name')
users
```

    <Table user (name, email, year_started)>

If no `pk` is provided, `id` is assumed to be the primary key.
Regardless of whether you mark a class as a dataclass or not, it will be
turned into one – specifically into a
[`flexiclass`](https://fastcore.fast.ai/xtras.html#flexiclass).

``` python
@dataclass
class Todo: id: int; title: str; detail: str; status: str; name: str
todos = db.create(Todo) 
todos
```

    <Table todo (id, title, detail, status, name)>

### Compound primary keys

The MiniData API spec supports compound primary keys, where more than
one column is used to identify records. We’ll also use this example to
demonstrate creating a table using a dict of keyword arguments.

``` python
class Publication: authors: str; year: int; title: str
publications = db.create(Publication, pk=('authors', 'year'))
```

### Transforming tables

Depending on the database type, this method can include transforms - the
ability to modify the tables. Let’s go ahead and add a password field
for our table called `pwd`.

``` python
class User: name:str; email: str; year_started:int; pwd:str
users = db.create(User, pk='name', transform=True)
users
```

    <Table user (name, email, year_started, pwd)>

## Manipulating data

The specification is designed to provide as straightforward CRUD API
(Create, Read, Update, and Delete) as possible. Additional features like
joins are out of scope.

### .insert()

Add a new record to the database. We want to support as many types as
possible, for now we have tests for Python classes, dataclasses, and
dicts. Returns an instance of the new record.

Here’s how to add a record using a Python class:

``` python
users.insert(User(name='Braden', email='b@example.com', year_started=2018))
```

    User(name='Braden', email='b@example.com', year_started=2018, pwd=None)

We can also use keyword arguments directly:

``` python
users.insert(name='Alma', email='a@example.com', year_started=2019)
```

    User(name='Alma', email='a@example.com', year_started=2019, pwd=None)

And now Charlie gets added via a Python dict.

``` python
users.insert({'name': 'Charlie', 'email': 'c@example.com', 'year_started': 2018})
```

    User(name='Charlie', email='c@example.com', year_started=2018, pwd=None)

And now TODOs. Note that the inserted row is returned:

``` python
todos.insert(Todo(title='Write MiniDataAPI spec', status='open', name='Braden'))
todos.insert(title='Implement SSE in FastHTML', status='open', name='Alma')
todo = todos.insert(dict(title='Finish development of FastHTML', status='closed', name='Charlie'))
todo
```

    Todo(id=3, title='Finish development of FastHTML', detail=None, status='closed', name='Charlie')

Let’s do the same with the `Publications` table.

``` python
publications.insert(Publication(authors='Alma', year=2019, title='FastHTML'))
publications.insert(authors='Alma', year=2030, title='FastHTML and beyond')
publication= publications.insert((dict(authors='Alma', year=2035, title='FastHTML, the early years')))
publication
```

    Publication(authors='Alma', year=2035, title='FastHTML, the early years')

### Square bracket search \[\]

Get a single record by entering a primary key into a table object within
square brackets. Let’s see if we can find Alma.

``` python
user = users['Alma']
user
```

    User(name='Alma', email='a@example.com', year_started=2019, pwd=None)

If no record is found, a `NotFoundError` error is raised. Here we look
for David, who hasn’t yet been added to our users table.

``` python
try: users['David']
except NotFoundError: print(f'User not found')
```

    User not found

Here’s a demonstration of a ticket search, demonstrating how this works
with non-string primary keys.

``` python
todos[1]
```

    Todo(id=1, title='Write MiniDataAPI spec', detail=None, status='open', name='Braden')

Compound primary keys can be supplied in lists or tuples, in the order
they were defined. In this case it is the `authors` and `year` columns.

Here’s a query by compound primary key done with a `list`:

``` python
publications[['Alma', 2019]]
```

    Publication(authors='Alma', year=2019, title='FastHTML')

Here’s the same query done directly with index args.

``` python
publications['Alma', 2030]
```

    Publication(authors='Alma', year=2030, title='FastHTML and beyond')

### Parentheses search ()

Get zero to many records by entering values with parentheses searches.
If nothing is in the parentheses, then everything is returned.

``` python
users()
```

    [User(name='Braden', email='b@example.com', year_started=2018, pwd=None),
     User(name='Alma', email='a@example.com', year_started=2019, pwd=None),
     User(name='Charlie', email='c@example.com', year_started=2018, pwd=None)]

We can order the results.

``` python
users(order_by='name')
```

    [User(name='Alma', email='a@example.com', year_started=2019, pwd=None),
     User(name='Braden', email='b@example.com', year_started=2018, pwd=None),
     User(name='Charlie', email='c@example.com', year_started=2018, pwd=None)]

We can filter on the results:

``` python
users(where="name='Alma'")
```

    [User(name='Alma', email='a@example.com', year_started=2019, pwd=None)]

Generally you probably want to use placeholders, to avoid SQL injection
attacks:

``` python
users("name=?", ('Alma',))
```

    [User(name='Alma', email='a@example.com', year_started=2019, pwd=None)]

We can limit results with the `limit` keyword:

``` python
users(limit=1)
```

    [User(name='Braden', email='b@example.com', year_started=2018, pwd=None)]

If we’re using the `limit` keyword, we can also use the `offset` keyword
to start the query later.

``` python
users(limit=5, offset=1)
```

    [User(name='Alma', email='a@example.com', year_started=2019, pwd=None),
     User(name='Charlie', email='c@example.com', year_started=2018, pwd=None)]

### .update()

Update an existing record of the database. Must accept Python dict,
dataclasses, and standard classes. Uses the primary key for identifying
the record to be changed. Returns an instance of the updated record.

Here’s with a normal Python class:

``` python
user
```

    User(name='Alma', email='a@example.com', year_started=2019, pwd=None)

``` python
user.year_started = 2099
users.update(user)
```

    User(name='Alma', email='a@example.com', year_started=2099, pwd=None)

Or use a dict:

``` python
users.update(dict(name='Alma', year_started=2199, email='a@example.com'))
```

    User(name='Alma', email='a@example.com', year_started=2199, pwd=None)

Or use kwargs:

``` python
users.update(name='Alma', year_started=2149)
```

    User(name='Alma', email='a@example.com', year_started=2149, pwd=None)

If the primary key doesn’t match a record, raise a `NotFoundError`.

John hasn’t started with us yet so doesn’t get the chance yet to travel
in time.

``` python
try: users.update(User(name='John', year_started=2024, email='j@example.com'))
except NotFoundError: print('User not found')
```

    User not found

### .delete()

Delete a record of the database. Uses the primary key for identifying
the record to be removed. Returns a table object.

Charlie decides to not travel in time. He exits our little group.

``` python
users.delete('Charlie')
```

    <Table user (name, email, year_started, pwd)>

If the primary key value can’t be found, raises a `NotFoundError`.

``` python
try: users.delete('Charlies')
except NotFoundError: print('User not found')
```

    User not found

In John’s case, he isn’t time travelling with us yet so can’t be
removed.

``` python
try: users.delete('John')
except NotFoundError: print('User not found')
```

    User not found

Deleting records with compound primary keys requires providing the
entire key.

``` python
publications.delete(['Alma' , 2035])
```

    <Table publication (authors, year, title)>

### `in` keyword

Are `Alma` and `John` contained `in` the Users table? Or, to be
technically precise, is the item with the specified primary key value
`in` this table?

``` python
'Alma' in users, 'John' in users
```

    (True, False)

Also works with compound primary keys, as shown below. You’ll note that
the operation can be done with either a `list` or `tuple`.

``` python
['Alma', 2019] in  publications
```

    True

And now for a `False` result, where John has no publications.

``` python
('John', 1967) in publications
```

    False

### .xtra()

If we set fields within the `.xtra` function to a particular value, then
indexing is also filtered by those. This applies to every database
method except for record creation. This makes it easier to limit users
(or other objects) access to only things for which they have permission.
This is a one-way operation, once set it can’t be undone for a
particular table object.

For example, if we query all our records below without setting values
via the `.xtra` function, we can see todos for everyone. Pay special
attention to the `id` values of all three records, as we are about to
filter most of them away.

``` python
todos()
```

    [Todo(id=1, title='Write MiniDataAPI spec', detail=None, status='open', name='Braden'),
     Todo(id=2, title='Implement SSE in FastHTML', detail=None, status='open', name='Alma'),
     Todo(id=3, title='Finish development of FastHTML', detail=None, status='closed', name='Charlie')]

Let’s use `.xtra` to constrain results just to Charlie. We set the
`name` field in Todos, but it could be any field defined for this table.

``` python
todos.xtra(name='Charlie')
```

We’ve now set a field to a value with `.xtra`, if we loop over all the
records again, only those assigned to records with a `name` of `Charlie`
will be displayed.

``` python
todos()
```

    [Todo(id=3, title='Finish development of FastHTML', detail=None, status='closed', name='Charlie')]

The `in` keyword is also affected. Only records with a `name` of Charlie
will evaluate to be `True`. Let’s demonstrate by testing it with a
Charlie record:

``` python
ct = todos[3]
ct
```

    Todo(id=3, title='Finish development of FastHTML', detail=None, status='closed', name='Charlie')

Charlie’s record has an ID of 3. Here we demonstrate that Charlie’s TODO
can be found in the list of todos:

``` python
ct.id in todos
```

    True

If we try `in` with the other IDs the query fails because the filtering
is now set to just records with a name of Charlie.

``` python
1 in todos, 2 in todos
```

    (False, False)

``` python
try: todos[2]
except NotFoundError: print('Record not found')
```

    Record not found

We are also constrained by what records we can update. In the following
example we try to update a TODO not named ‘Charlie’. Because the name is
wrong, the `.update` function will raise a `NotFoundError`.

``` python
try: todos.update(Todo(id=1, title='Finish MiniDataAPI Spec', status='closed', name='Braden'))
except NotFoundError as e: print('Record not updated')
```

    Record not updated

Unlike poor Braden, Charlie isn’t filtered out. Let’s update his TODO.

``` python
todos.update(Todo(id=3, title='Finish development of FastHTML', detail=None, status='closed', name='Charlie'))
```

    Todo(id=3, title='Finish development of FastHTML', detail=None, status='closed', name='Charlie')

Finally, once constrained by `.xtra`, only records with Charlie as the
name can be deleted.

``` python
try: todos.delete(1)
except NotFoundError as e: print('Record not updated')
```

    Record not updated

Charlie’s TODO was to finish development of FastHTML. While the
framework will stabilize, like any good project it will see new features
added and the odd bug corrected for many years to come. Therefore,
Charlie’s TODO is nonsensical. Let’s delete it.

``` python
todos.delete(ct.id)
```

    <Table todo (id, title, detail, status, name)>

When a TODO is inserted, the `xtra` fields are automatically set. This
ensures that we don’t accidentally, for instance, insert items for
others users. Note that here we don’t set the `name` field, but it’s
still included in the resultant row:

``` python
ct = todos.insert(Todo(title='Rewrite personal site in FastHTML', status='open'))
ct
```

    Todo(id=3, title='Rewrite personal site in FastHTML', detail=None, status='open', name='Charlie')

If we try to change the username to someone else, the change is ignored,
due to `xtra`:

``` python
ct.name = 'Braden'
todos.update(ct)
```

    Todo(id=3, title='Rewrite personal site in FastHTML', detail=None, status='open', name='Charlie')

## SQL-first design

``` python
users = None
User = None
```

``` python
users = db.t.user
users
```

    <Table user (name, email, year_started, pwd)>

(This section needs to be documented properly.)

From the table objects we can extract a Dataclass version of our tables.
Usually this is given an singular uppercase version of our table name,
which in this case is `User`.

``` python
User = users.dataclass()
```

``` python
User(name='Braden', email='b@example.com', year_started=2018)
```

    User(name='Braden', email='b@example.com', year_started=2018, pwd=UNSET)

## Implementations

### Implementing MiniDataAPI for a new datastore

For creating new implementations, the code examples in this
specification are the test case for the API. New implementations should
pass the tests in order to be compliant with the specification.

### Implementations

- [fastlite](https://github.com/AnswerDotAI/fastlite) - The original
  implementation, only for Sqlite
- [fastsql](https://github.com/AnswerDotAI/fastsql) - An SQL database
  agnostic implementation based on the excellent SQLAlchemy library.</doc><doc title="OAuth" desc="Tutorial and explanation of how to use OAuth in FastHTML apps."># OAuth



OAuth is an open standard for ‘access delegation’, commonly used as a
way for Internet users to grant websites or applications access to their
information on other websites but without giving them the passwords. It
is the mechanism that enables “Log in with Google” on many sites, saving
you from having to remember and manage yet another password. Like many
auth-related topics, there’s a lot of depth and complexity to the OAuth
standard, but once you understand the basic usage it can be a very
convenient alternative to managing your own user accounts.

On this page you’ll see how to use OAuth with FastHTML to implement some
common pieces of functionality.

## Creating an Client

FastHTML has Client classes for managing settings and state for
different OAuth providers. Currently implemented are: GoogleAppClient,
GitHubAppClient, HuggingFaceClient and DiscordAppClient - see the
[source](https://github.com/AnswerDotAI/fasthtml/blob/main/nbs/api/08_oauth.ipynb)
if you need to add other providers. You’ll need a `client_id` and
`client_secret` from the provider (see the from-scratch example later in
this page for an example of registering with GitHub) to create the
client. We recommend storing these in environment variables, rather than
hardcoding them in your code.

``` python
import os
from fasthtml.oauth import GoogleAppClient
client = GoogleAppClient(os.getenv("AUTH_CLIENT_ID"),
                         os.getenv("AUTH_CLIENT_SECRET"))
```

The client is used to obtain a login link and to manage communications
between your app and the OAuth provider
(`client.login_link(redirect_uri="/redirect")`).

## Using the OAuth class

Once you’ve set up a client, adding OAuth to a FastHTML app can be as
simple as:

``` python
from fasthtml.oauth import OAuth
from fasthtml.common import FastHTML, RedirectResponse

class Auth(OAuth):
    def get_auth(self, info, ident, session, state):
        email = info.email or ''
        if info.email_verified and email.split('@')[-1]=='answer.ai':
            return RedirectResponse('/', status_code=303)

app = FastHTML()
oauth = Auth(app, client)

@app.get('/')
def home(auth): return P('Logged in!'), A('Log out', href='/logout')

@app.get('/login')
def login(req): return Div(P("Not logged in"), A('Log in', href=oauth.login_link(req)))
```

There’s a fair bit going on here, so let’s unpack what’s happening in
that code:

- OAuth (and by extension our custom Auth class) has a number of default
  arguments, including some key URLs:
  `redir_path='/redirect', error_path='/error', logout_path='/logout', login_path='/login'`.
  It will create and handle the redirect and logout paths, and it’s up
  to you to handle `/login` (where unsuccessful login attempts will be
  redirected) and `/error` (for oauth errors).
- When we run `oauth = Auth(app, client)` it adds the redirect and
  logout paths to the app and also adds some beforeware. This beforeware
  runs on any requests (apart from any specified with the `skip`
  parameter).

The added beforeware specifies some app behaviour:

- If someone who isn’t logged in attempts to visit our homepage (`/`)
  here, they will be redirected to `/login`.
- If they are logged in, it calls a `check_invalid` method. This
  defaults to False, which let’s the user continue to the page they
  requested. The behaviour can be modified by defining your own
  `check_invalid` method in the Auth class - for example, you could have
  this forcibly log out users who have recently been banned.

So how does someone log in? If they visit (or are redirected to) the
login page at `/login`, we show them a login link. This sends them to
the OAuth provider, where they’ll go through the steps of selecting
their account, giving permissions etc. Once done they will be redirected
back to `/redirect`. Behind the scenes a code that comes as part of
their request gets turned into user info, which is then passed to the
key function `get_auth(self, info, ident, session, state)`. Here is
where you’d handle looking up or adding a user in a database, checking
for some condition (for example, this code checks if the email is an
answer.ai email address) or choosing the destination based on state. The
arguments are:

- `self`: the Auth object, which you can use to access the client
  (`self.cli`)
- `info`: the information provided by the OAuth provider, typically
  including a unique user id, email address, username and other
  metadata.
- `ident`: a unique identifier for this user. What this looks like
  varies between providers. This is useful for managing a database of
  users, for example.
- `session`: the current session, that you can store information in
  securely
- `state`: you can optionally pass in some state when creating the login
  link. This persists and is returned after the user goes through the
  Oath steps, which is useful for returning them to the same page they
  left. It can also be used as added security against CSRF attacks.

In our example, we check the email in `info` (we use a GoogleAppClient,
not all providers will include an email). If we aren’t happy, and
get_auth returns False or nothing (as in the case here for non-answerai
people) then the user is redirected back to the login page. But if
everything looks good we return a redirect to the homepage, and an
`auth` key is added to the session and the scope containing the users
identity `ident`. So, for example, in the homepage route we could use
`auth` to look up this particular user’s profile info and customize the
page accordingly. This auth will persist in their session until they
clear the browser cache, so by default they’ll stay logged in. To log
them out, remove it ( `session.pop('auth', None)`) or send them to
`/logout` which will do that for you.

## Explaining OAuth with a from-scratch implementation

Hopefully the example above is enough to get you started. You can also
check out the (fairly minimal) [source
code](https://github.com/AnswerDotAI/fasthtml/blob/main/nbs/api/08_oauth.ipynb)
where this is implemented, and the [examples
here](https://github.com/AnswerDotAI/fasthtml-example/blob/main/oauth_example).

If you’re wanting to learn more about how this works, and to see where
you might add additional functionality, the rest of this page will walk
through some examples **without** the OAuth convenience class, to
illustrate the concepts. This was written before said OAuth class was
available, and is kept here for educational purposes - we recommend you
stick with the new approach shown above in most cases.

## A Minimal Login Flow (GitHub)

Let’s begin by building a minimal ‘Sign in with GitHub’ flow. This will
demonstrate the basic steps of OAuth.

OAuth requires a “provider” (in this case, GitHub) to authenticate the
user. So the first step when setting up our app is to register with
GitHub to set things up.

Go to https://github.com/settings/developers and click “New OAuth App”.
Fill in the form with the following values, then click ‘Register
application’.

- Application name: Your app name
- Homepage URL: http://localhost:8000 (or whatever URL you’re using -
  you can change this later)
- Authorization callback URL: http://localhost:8000/auth_redirect (you
  can modify this later too)

<div style="text-align:center;margin:50px 0 45px;">

<img src="imgs/gh-oauth.png" alt="Setting up an OAuth app in GitHub" width="500" />

</div>

After you register, you’ll see a screen where you can view the client ID
and generate a client secret. Store these values in a safe place. You’ll
use them to create a
[`GitHubAppClient`](https://www.fastht.ml/docs/api/oauth.html#githubappclient)
object in FastHTML.

This `client` object is responsible for handling the parts of the OAuth
flow which depend on direct communication between your app and GitHub,
as opposed to interactions which go through the user’s browser via
redirects.

Here is how to setup the client object:

``` python
client = GitHubAppClient(
    client_id="your_client_id",
    client_secret="your_client_secret"
)
```

You should also save the path component of the authorization callback
URL which you provided on registration.

This route is where GitHub will redirect the user’s browser in order to
send an authorization code to your app. You should save only the URL’s
path component rather than the entire URL because you want your code to
work automatically in deployment, when the host and port part of the URL
change from `localhost:8000` to your real DNS name.

Save the special authorization callback path under an obvious name:

``` python
auth_callback_path = "/auth_redirect"
```

<div>

> **Note**
>
> It’s recommended to store the client ID, and secret, in environment
> variables, rather than hardcoding them in your code.

</div>

When the user visit a normal page of your app, if they are not already
logged in, then you’ll want to redirect them to your app’s login page,
which will live at the `/login` path. We accomplish that by using this
piece of “beforeware”, which defines logic which runs before other work
for all routes except ones we specify to be skipped:

``` python
def before(req, session):
    auth = req.scope['auth'] = session.get('user_id', None)
    if not auth: return RedirectResponse('/login', status_code=303)
    counts.xtra(name=auth)
bware = Beforeware(before, skip=['/login', auth_callback_path])
```

We configure the beforeware to skip `/login` because that’s where the
user goes to login, and we also skip the special authorization callback
path because that is used by OAuth itself to receive information from
GitHub.

It’s only at your login page that we start the OAuth flow. To start the
OAuth flow, you need to give the user a link to GitHub’s login for your
app. You’ll need the `client` object to generate that link, and the
client object will in turn need the full authorization callback URL,
which we need to build from the authorization callback path, so it is a
multi-step process to produce this GitHub login link.

Here is an implementation of your own `/login` route handler. It
generates the GitHub login link and presents it to the user:

``` python
@app.get('/login')
def login(request)
    redir = redir_url(request,auth_callback_path)
    login_link = client.login_link(redir)
    return P(A('Login with GitHub', href=login_link))    
```

Once the user follows that link, GitHub will ask them to grant
permission to your app to access their GitHub account. If they agree,
GitHub will redirect them back to your app’s authorization callback URL,
carrying an authorization code which your app can use to generate an
access token. To receive this code, you need to set up a route in
FastHTML that listens for requests at the authorization callback path.
For example:

``` python
@app.get(auth_callback_path)
def auth_redirect(code:str):
    return P(f"code: {code}")
```

This authorization code is temporary, and is used by your app to
directly ask the provider for user information like an access token.

To recap, you can think of the exchange so far as:

- User to us: “I want to log in with you, app.”
- Us to User: “Okay but first, here’s a special link to log in with
  GitHub”
- User to GitHub: “I want to log in with you, GitHub, to use this app.”
- GitHub to User: “OK, redirecting you back to the app’s URL (with an
  auth code)”
- User to Us: “Hi again, app. Here’s the GitHub auth code you need to
  ask GitHub for info about me” (delivered via
  `/auth_redirect?code=...`)

The final steps we need to implement are as follows:

- Us to GitHUb: “A user just gave me this auth code. May I have the user
  info (e.g., an access token)?”
- GitHub to us: “Since you have an auth code, here’s the user info”

It’s critical for us to derive the user info from the auth code
immediately in the authorization callback, because the auth code may be
used only once. So we use it that once in order to get information like
an access token, which will remain valid for longer.

To go from the auth code to user info, you use
`info = client.retr_info(code,redirect_uri)`. From the user info, you
can extract the `user_id`, which is a unique identifier for the user:

``` python
@app.get(auth_callback_path)
def auth_redirect(code:str, request):
    redir = redir_url(request, auth_callback_path)
    user_info = client.retr_info(code, redir)
    user_id = info[client.id_key]
    return P(f"User id: {user_id}")
```

But we want the user ID not to print it but to remember the user.

So let us store it in the `session` object, to remember who is logged
in:

``` python
@app.get(auth_callback_path)
def auth_redirect(code:str, request, session):
    redir = redir_url(request, auth_callback_path)
    user_info = client.retr_info(code, redir)
    user_id = user_info[client.id_key] # get their ID
    session['user_id'] = user_id # save ID in the session
    return RedirectResponse('/', status_code=303)
```

The session object is derived from values visible to the user’s browser,
but it is cryptographically signed so the user can’t read it themselves.
This makes it safe to store even information we don’t want to expose to
the user.

For larger quantities of data, we’d want to save that information in a
database and use the session to hold keys to lookup information from
that database.

Here’s a minimal app that puts all these pieces together. It uses the
user info to get the user_id. It stores that in the session object. It
then uses the user_id as a key into a database, which tracks how
frequently every user has hit an increment button.

``` python
import os
from fasthtml.common import *
from fasthtml.oauth import GitHubAppClient, redir_url

db = database('data/counts.db')
counts = db.t.counts
if counts not in db.t: counts.create(dict(name=str, count=int), pk='name')
Count = counts.dataclass()

# Auth client setup for GitHub
client = GitHubAppClient(os.getenv("AUTH_CLIENT_ID"), 
                         os.getenv("AUTH_CLIENT_SECRET"))
auth_callback_path = "/auth_redirect"

def before(req, session):
    # if not logged in, we send them to our login page
    # logged in means:
    # - 'user_id' in the session object, 
    # - 'auth' in the request object
    auth = req.scope['auth'] = session.get('user_id', None)
    if not auth: return RedirectResponse('/login', status_code=303)
    counts.xtra(name=auth)
bware = Beforeware(before, skip=['/login', auth_callback_path])

app = FastHTML(before=bware)

# User asks us to Login
@app.get('/login')
def login(request):
    redir = redir_url(request,auth_callback_path)
    login_link = client.login_link(redir)
    # we tell user to login at github
    return P(A('Login with GitHub', href=login_link))    

# User comes back to us with an auth code from Github
@app.get(auth_callback_path)
def auth_redirect(code:str, request, session):
    redir = redir_url(request, auth_callback_path)
    user_info = client.retr_info(code, redir)
    user_id = user_info[client.id_key] # get their ID
    session['user_id'] = user_id # save ID in the session
    # create a db entry for the user
    if user_id not in counts: counts.insert(name=user_id, count=0)
    return RedirectResponse('/', status_code=303)

@app.get('/')
def home(auth):
    return Div(
        P("Count demo"),
        P(f"Count: ", Span(counts[auth].count, id='count')),
        Button('Increment', hx_get='/increment', hx_target='#count'),
        P(A('Logout', href='/logout'))
    )

@app.get('/increment')
def increment(auth):
    c = counts[auth]
    c.count += 1
    return counts.upsert(c).count

@app.get('/logout')
def logout(session):
    session.pop('user_id', None)
    return RedirectResponse('/login', status_code=303)

serve()
```

Some things to note:

- The [`before`](https://www.fastht.ml/docs/explains/stripe.html#before)
  function is used to check if the user is authenticated. If not, they
  are redirected to the login page.
- To log the user out, we remove the user ID from the session.
- Calling `counts.xtra(name=auth)` ensures that only the row
  corresponding to the current user is accessible when responding to a
  request. This is often nicer than trying to remember to filter the
  data in every route, and lowers the risk of accidentally leaking data.
- In the `auth_redirect` route, we store the user ID in the session and
  create a new row in the `user_counts` table if it doesn’t already
  exist.

You can find more heavily-commented version of this code in the [oauth
directory in
fasthtml-example](https://github.com/AnswerDotAI/fasthtml-example/tree/main/oauth_example),
along with an even more minimal example. More examples may be added in
the future.

### Revoking Tokens (Google)

When the user in the example above logs out, we remove their user ID
from the session. However, the user is still logged in to GitHub. If
they click ‘Login with GitHub’ again, they’ll be redirected back to our
site without having to log in again. This is because GitHub remembers
that they’ve already granted our app permission to access their account.
Most of the time this is convenient, but for testing or security
purposes you may want a way to revoke this permission.

As a user, you can usually revoke access to an app from the provider’s
website (for example, <https://github.com/settings/applications>). But
as a developer, you can also revoke access programmatically - at least
with some providers. This requires keeping track of the access token
(stored in `client.token["access_token"]` after you call `retr_info`),
and sending a request to the provider’s revoke URL:

``` python
auth_revoke_url = "https://accounts.google.com/o/oauth2/revoke"
def revoke_token(token):
    response = requests.post(auth_revoke_url, params={"token": token})
    return response.status_code == 200 # True if successful
```

Not all providers support token revocation, and it is not built into
FastHTML clients at the moment.

### Using State (Hugging Face)

Imagine a user (not logged in) comes to your AI image editing site,
starts testing things out, and then realizes they need to sign in before
they can click “Run (Pro)” on the edit they’re working on. They click
“Sign in with Hugging Face”, log in, and are redirected back to your
site. But now they’ve lost their in-progress edit and are left just
looking at the homepage! This is an example of a case where you might
want to keep track of some additional state. Another strong use case for
being able to pass some uniqie state through the OAuth flow is to
prevent something called a [CSRF
attack](https://en.wikipedia.org/wiki/Cross-site_request_forgery). To
add a state string to the OAuth flow, include a `state` argument when
creating the login link:

``` python
# in login page:
link = A('Login with GitHub', href=client.login_link(state='current_prompt: add a unicorn'))

# in auth_redirect:
@app.get('/auth_redirect')
def auth_redirect(code:str, session, state:str=None):
    print(f"state: {state}") # Use as needed
    ...
```

The state string is passed through the OAuth flow and back to your site.

### A Work in Progress

This page (and OAuth support in FastHTML) is a work in progress.
Questions, PRs, and feedback are welcome!</doc><doc title="Routes" desc="Explanation of how routes work in FastHTML."># Routes



Behaviour in FastHTML apps is defined by routes. The syntax is largely
the same as the wonderful [FastAPI](https://fastapi.tiangolo.com/)
(which is what you should be using instead of this if you’re creating a
JSON service. FastHTML is mainly for making HTML web apps, not APIs).

<div>

> **Unfinished**
>
> We haven’t yet written complete documentation of all of FastHTML’s
> routing features – until we add that, the best place to see all the
> available functionality is to look over [the
> tests](../api/core.html#tests)

</div>

Note that you need to include the types of your parameters, so that
[`FastHTML`](https://www.fastht.ml/docs/api/core.html#fasthtml) knows
what to pass to your function. Here, we’re just expecting a string:

``` python
from fasthtml.common import *
```

``` python
app = FastHTML()

@app.get('/user/{nm}')
def get_nm(nm:str): return f"Good day to you, {nm}!"
```

Normally you’d save this into a file such as main.py, and then run it in
`uvicorn` using:

    uvicorn main:app

However, for testing, we can use Starlette’s `TestClient` to try it out:

``` python
from starlette.testclient import TestClient
```

``` python
client = TestClient(app)
r = client.get('/user/Jeremy')
r
```

    <Response [200 OK]>

TestClient uses `httpx` behind the scenes, so it returns a
`httpx.Response`, which has a `text` attribute with our response body:

``` python
r.text
```

    'Good day to you, Jeremy!'

In the previous example, the function name (`get_nm`) didn’t actually
matter – we could have just called it `_`, for instance, since we never
actually call it directly. It’s just called through HTTP. In fact, we
often do call our functions `_` when using this style of route, since
that’s one less thing we have to worry about, naming.

An alternative approach to creating a route is to use `app.route`
instead, in which case, you make the function name the HTTP method you
want. Since this is such a common pattern, you might like to give a
shorter name to `app.route` – we normally use `rt`:

``` python
rt = app.route

@rt('/')
def post(): return "Going postal!"

client.post('/').text
```

    'Going postal!'

### Route-specific functionality

FastHTML supports custom decorators for adding specific functionality to
routes. This allows you to implement authentication, authorization,
middleware, or other custom behaviors for individual routes.

Here’s an example of a basic authentication decorator:

``` python
from functools import wraps

def basic_auth(f):
    @wraps(f)
    async def wrapper(req, *args, **kwargs):
        token = req.headers.get("Authorization")
        if token == 'abc123':
            return await f(req, *args, **kwargs)
        return Response('Not Authorized', status_code=401)
    return wrapper

@app.get("/protected")
@basic_auth
async def protected(req):
    return "Protected Content"

client.get('/protected', headers={'Authorization': 'abc123'}).text
```

    'Protected Content'

The decorator intercepts the request before the route function executes.
If the decorator allows the request to proceed, it calls the original
route function, passing along the request and any other arguments.

One of the key advantages of this approach is the ability to apply
different behaviors to different routes. You can also stack multiple
decorators on a single route for combined functionality.

``` python
def app_beforeware():
    print('App level beforeware')

app = FastHTML(before=Beforeware(app_beforeware))
client = TestClient(app)

def route_beforeware(f):
    @wraps(f)
    async def decorator(*args, **kwargs):
        print('Route level beforeware')
        return await f(*args, **kwargs)
    return decorator
    
def second_route_beforeware(f):
    @wraps(f)
    async def decorator(*args, **kwargs):
        print('Second route level beforeware')
        return await f(*args, **kwargs)
    return decorator

@app.get("/users")
@route_beforeware
@second_route_beforeware
async def users():
    return "Users Page"

client.get('/users').text
```

    App level beforeware
    Route level beforeware
    Second route level beforeware

    'Users Page'

This flexiblity allows for granular control over route behaviour,
enabling you to tailor each endpoint’s functionality as needed. While
app-level beforeware remains useful for global operations, decorators
provide a powerful tool for route-specific customization.

## Combining Routes

Sometimes a FastHTML project can grow so weildy that putting all the
routes into `main.py` becomes unweildy. Or, we install a FastHTML- or
Starlette-based package that requires us to add routes.

First let’s create a `books.py` module, that represents all the
user-related views:

``` python
# books.py
books_app, rt = fast_app()

books = ['A Guide to FastHTML', 'FastHTML Cookbook', 'FastHTML in 24 Hours']

@rt("/", name="list")
def get():
    return Titled("Books", *[P(book) for book in books])
```

Let’s mount it in our main module:

``` python
from books import books_app

app, rt = fast_app(routes=[Mount("/books", books_app, name="books")])

@rt("/")
def get():
    return Titled("Dashboard",
        P(A(href="/books")("Books")),
        Hr(),
        P(A(link=uri("books:list"))("Books")),
    )

serve()
```

Line 3  
We use `starlette.Mount` to add the route to our routes list. We provide
the name of `books` to make discovery and management of the links
easier. More on that in items 2 and 3 of this annotations list

Line 8  
This example link to the books list view is hand-crafted. Obvious in
purpose, it makes changing link patterns in the future harder

Line 10  
This example link uses the named URL route for the books. The advantage
of this approach is it makes management of large numbers of link items
easier.</doc><doc title="WebSockets" desc="Explanation of websockets and how they work in FastHTML."># WebSockets



Websockets are a protocol for two-way, persistent communication between
a client and server. This is different from HTTP, which uses a
request/response model where the client sends a request and the server
responds. With websockets, either party can send messages at any time,
and the other party can respond.

This allows for different applications to be built, including things
like chat apps, live-updating dashboards, and real-time collaborative
tools, which would require constant polling of the server for updates
with HTTP.

In FastHTML, you can create a websocket route using the `@app.ws`
decorator. This decorator takes a route path, and optional `conn` and
`disconn` parameters representing the `on_connect` and `on_disconnect`
callbacks in websockets, respectively. The function decorated by
`@app.ws` is the main function that is called when a message is
received.

Here’s an example of a basic websocket route:

``` python
@app.ws('/ws', conn=on_conn, disconn=on_disconn)
async def on_message(msg:str, send):
    await send(Div('Hello ' + msg, id='notifications'))
    await send(Div('Goodbye ' + msg, id='notifications'))
```

The `on_message` function is the main function that is called when a
message is received and can be named however you like. Similar to
standard routes, the arguments to `on_message` are automatically parsed
from the websocket payload for you, so you don’t need to manually parse
the message content. However, certain argument names are reserved for
special purposes. Here are the most important ones:

- `send` is a function that can be used to send text data to the client.
- `data` is a dictionary containing the data sent by the client.
- `ws` is a reference to the websocket object.

For example, we can send a message to the client that just connected
like this:

``` python
async def on_conn(send):
    await send(Div('Hello, world!'))
```

Or if we receive a message from the client, we can send a message back
to them:

``` python
@app.ws('/ws', conn=on_conn, disconn=on_disconn)
async def on_message(msg:str, send):
    await send(Div('You said: ' + msg, id='notifications'))
    # or...
    return Div('You said: ' + msg, id='notifications')
```

On the client side, we can use HTMX’s websocket extension to open a
websocket connection and send/receive messages. For example:

``` python
from fasthtml.common import *

app = FastHTML(exts='ws')

@app.get('/')
def home():
    cts = Div(
        Div(id='notifications'),
        Form(Input(id='msg'), id='form', ws_send=True),
        hx_ext='ws', ws_connect='/ws')
    return Titled('Websocket Test', cts)
```

This will create a websocket connection to the server on route `/ws`,
and send any form submissions to the server via the websocket. The
server will then respond by sending a message back to the client. The
client will then update the message div with the message from the server
using Out of Band Swaps, which means that the content is swapped with
the same id without reloading the page.

<div>

> **Note**
>
> Make sure you set `exts='ws'` when creating your
> [`FastHTML`](https://www.fastht.ml/docs/api/core.html#fasthtml) object
> if you want to use websockets so the extension is loaded.

</div>

Putting it all together, the code for the client and server should look
like this:

``` python
from fasthtml.common import *

app = FastHTML(exts='ws')
rt = app.route

@rt('/')
def get():
    cts = Div(
        Div(id='notifications'),
        Form(Input(id='msg'), id='form', ws_send=True),
        hx_ext='ws', ws_connect='/ws')
    return Titled('Websocket Test', cts)

@app.ws('/ws')
async def ws(msg:str, send):
    await send(Div('Hello ' + msg, id='notifications'))

serve()
```

This is a fairly simple example and could be done just as easily with
standard HTTP requests, but it illustrates the basic idea of how
websockets work. Let’s look at a more complex example next.

## Session data in Websockets

Session data is shared between standard HTTP routes and Websockets. This
means you can access, for example, logged in user ID inside websocket
handler:

``` python
from fasthtml.common import *

app = FastHTML(exts='ws')
rt = app.route

@rt('/login')
def get(session):
    session["person"] = "Bob"
    return "ok"

@app.ws('/ws')
async def ws(msg:str, send, session):
    await send(Div(f'Hello {session.get("person")}' + msg, id='notifications'))

serve()
```

## Real-Time Chat App

Let’s put our new websocket knowledge to use by building a simple chat
app. We will create a chat app where multiple users can send and receive
messages in real time.

Let’s start by defining the app and the home page:

``` python
from fasthtml.common import *

app = FastHTML(exts='ws')
rt = app.route

msgs = []
@rt('/')
def home(): return Div(
    Div(Ul(*[Li(m) for m in msgs], id='msg-list')),
    Form(Input(id='msg'), id='form', ws_send=True),
    hx_ext='ws', ws_connect='/ws')
```

Now, let’s handle the websocket connection. We’ll add a new route for
this along with an `on_conn` and `on_disconn` function to keep track of
the users currently connected to the websocket. Finally, we will handle
the logic for sending messages to all connected users.

``` python
users = {}
def on_conn(ws, send): users[str(id(ws))] = send
def on_disconn(ws): users.pop(str(id(ws)), None)

@app.ws('/ws', conn=on_conn, disconn=on_disconn)
async def ws(msg:str):
    msgs.append(msg)
    # Use associated `send` function to send message to each user
    for u in users.values(): await u(Ul(*[Li(m) for m in msgs], id='msg-list'))

serve()
```

We can now run this app with `python chat_ws.py` and open multiple
browser tabs to `http://localhost:5001`. You should be able to send
messages in one tab and see them appear in the other tabs.

### A Work in Progress

This page (and Websocket support in FastHTML) is a work in progress.
Questions, PRs, and feedback are welcome!</doc><doc title="Custom Components" desc="Explanation of how to create custom components in FastHTML."># Custom Components



The majority of the time the default [ft
components](../explains/explaining_xt_components.html) are all you need
(for example `Div`, `P`, `H1`, etc.).

<div>

> **Pre-requisite Knowledge**
>
> If you don’t know what an ft component is, you should read [the
> explaining ft components explainer
> first](../explains/explaining_xt_components.html).

</div>

However, there are many situations where you need a custom ft component
that creates a unique HTML tag (for example `<zero-md></zero-md>`).
There are many options in FastHTML to do this, and this section will
walk through them. Generally you want to use the highest level option
that fits your needs.

<div>

> **Real-world example**
>
> [This external
> tutorial](https://isaac-flath.github.io/website/posts/boots/FasthtmlTutorial.html)
> walks through a practical situation where you may want to create a
> custom HTML tag using a custom ft component. Seeing a real-world
> example is a good way to understand why the contents of this guide is
> useful.

</div>

## NotStr

The first way is to use the `NotStr` class to use an HTML tag as a
string. It works as a one-off but quickly becomes harder to work with as
complexity grows. However we can see that you can genenrate the same xml
using `NotStr` as the out-of-the-box components.

``` python
from fasthtml.common import NotStr,Div, to_xml
```

``` python
div_NotStr = NotStr('<div></div>') 
print(div_NotStr)
```

    <div></div>

## Automatic Creation

The next (and better) approach is to let FastHTML generate the component
function for you. As you can see in our `assert` this creates a function
that creates the HTML just as we wanted. This works even though there is
not a `Some_never_before_used_tag` function in the `fasthtml.components`
source code (you can verify this yourself by looking at the source
code).

<div>

> **Tip**
>
> Typically these tags are needed because a CSS or Javascript library
> created a new XML tag that isn’t default HTML. For example the
> `zero-md` javascript library looks for a `<zero-md></zero-md>` tag to
> know what to run its javascript code on. Most CSS libraries work by
> creating styling based on the `class` attribute, but they can also
> apply styling to an arbitrary HTML tag that they made up.

</div>

``` python
from fasthtml.components import Some_never_before_used_tag

Some_never_before_used_tag()
```

``` html
<some-never-before-used-tag></some-never-before-used-tag>
```

## Manual Creation

The automatic creation isn’t magic. It’s just calling a python function
[`__getattr__`](https://www.fastht.ml/docs/api/components.html#__getattr__)
and you can call it yourself to get the same result.

``` python
import fasthtml

auto_called = fasthtml.components.Some_never_before_used_tag()
manual_called = fasthtml.components.__getattr__('Some_never_before_used_tag')()

# Proving they generate the same xml
assert to_xml(auto_called) == to_xml(manual_called)
```

Knowing that, we know that it’s possible to create a different function
that has different behavior than FastHTMLs default behavior by modifying
how the `___getattr__` function creates the components! It’s only a few
lines of code and reading that what it does is a great way to understand
components more deeply.

<div>

> **Tip**
>
> Dunder methods and functions are special functions that have double
> underscores at the beginning and end of their name. They are called at
> specific times in python so you can use them to cause customized
> behavior that makes sense for your specific use case. They can appear
> magical if you don’t know how python works, but they are extremely
> commonly used to modify python’s default behavior (`__init__` is
> probably the most common one).
>
> In a module
> [`__getattr__`](https://www.fastht.ml/docs/api/components.html#__getattr__)
> is called to get an attribute. In `fasthtml.components`, this is
> defined to create components automatically for you.

</div>

For example if you want a component that creates `<path></path>` that
doesn’t conflict names with
[`pathlib.Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)
you can do that. FastHTML automatically creates new components with a
1:1 mapping and a consistent name, which is almost always what you want.
But in some cases you may want to customize that and you can use the
[`ft_hx`](https://www.fastht.ml/docs/api/components.html#ft_hx) function
to do that differently than the default.

``` python
from fasthtml.common import ft_hx

def ft_path(*c, target_id=None, **kwargs): 
    return ft_hx('path', *c, target_id=target_id, **kwargs)

ft_path()
```

``` html
<path></path>
```

We can add any behavior in that function that we need to, so let’s go
through some progressively complex examples that you may need in some of
your projects.

### Underscores in tags

Now that we understand how FastHTML generates components, we can create
our own in all kinds of ways. For example, maybe we need a weird HTML
tag that uses underscores. FastHTML replaces `_` with `-` in tags
because underscores in tags are highly unusual and rarely what you want,
though it does come up rarely.

``` python
def tag_with_underscores(*c, target_id=None, **kwargs): 
    return ft_hx('tag_with_underscores', *c, target_id=target_id, **kwargs)

tag_with_underscores()
```

``` html
<tag_with_underscores></tag_with_underscores>
```

### Symbols (ie @) in tags

Sometimes you may need to use a tag that uses characters that are not
allowed in function names in python (again, very unusual).

``` python
def tag_with_AtSymbol(*c, target_id=None, **kwargs): 
    return ft_hx('tag-with-@symbol', *c, target_id=target_id, **kwargs)

tag_with_AtSymbol()
```

``` html
<tag-with-@symbol></tag-with-@symbol>
```

### Symbols (ie @) in tag attributes

It also may be that an argument in an HTML tag uses characters that
can’t be used in python arguments. To handle these you can define those
args using a dictionary.

``` python
Div(normal_arg='normal stuff',**{'notNormal:arg:with_varing@symbols!':'123'})
```

``` html
<div normal-arg="normal stuff" notnormal:arg:with_varing@symbols!="123"></div>
```</doc><doc title="Handling Handlers" desc="Explanation of how to request and response handlers work in FastHTML as routes."># Handling handlers



``` python
from fasthtml.common import *
from collections import namedtuple
from typing import TypedDict
from datetime import datetime
import json,time
```

``` python
app = FastHTML()
```

The [`FastHTML`](https://www.fastht.ml/docs/api/core.html#fasthtml)
class is the main application class for FastHTML apps.

``` python
rt = app.route
```

`app.route` is used to register route handlers. It is a decorator, which
means we place it before a function that is used as a handler. Because
it’s used frequently in most FastHTML applications, we often alias it as
`rt`, as we do here.

## Basic Route Handling

``` python
@rt("/hi")
def get(): return 'Hi there'
```

Handler functions can return strings directly. These strings are sent as
the response body to the client.

``` python
cli = Client(app)
```

[`Client`](https://www.fastht.ml/docs/api/core.html#client) is a test
client for FastHTML applications. It allows you to simulate requests to
your app without running a server.

``` python
cli.get('/hi').text
```

    'Hi there'

The `get` method on a
[`Client`](https://www.fastht.ml/docs/api/core.html#client) instance
simulates GET requests to the app. It returns a response object that has
a `.text` attribute, which you can use to access the body of the
response. It calls `httpx.get` internally – all httpx HTTP verbs are
supported.

``` python
@rt("/hi")
def post(): return 'Postal'
cli.post('/hi').text
```

    'Postal'

Handler functions can be defined for different HTTP methods on the same
route. Here, we define a
[`post`](https://www.fastht.ml/docs/explains/stripe.html#post) handler
for the `/hi` route. The
[`Client`](https://www.fastht.ml/docs/api/core.html#client) instance can
simulate different HTTP methods, including POST requests.

## Request and Response Objects

``` python
@app.get("/hostie")
def show_host(req): return req.headers['host']
cli.get('/hostie').text
```

    'testserver'

Handler functions can accept a `req` (or `request`) parameter, which
represents the incoming request. This object contains information about
the request, including headers. In this example, we return the `host`
header from the request. The test client uses ‘testserver’ as the
default host.

In this example, we use `@app.get("/hostie")` instead of
`@rt("/hostie")`. The `@app.get()` decorator explicitly specifies the
HTTP method (GET) for the route, while `@rt()` by default handles both
GET and POST requests.

``` python
@rt
def yoyo(): return 'a yoyo'
cli.post('/yoyo').text
```

    'a yoyo'

If the `@rt` decorator is used without arguments, it uses the function
name as the route path. Here, the `yoyo` function becomes the handler
for the `/yoyo` route. This handler responds to GET and POST methods,
since a specific method wasn’t provided.

``` python
@rt
def ft1(): return Html(Div('Text.'))
print(cli.get('/ft1').text)
```

     <!doctype html>
     <html>
       <div>Text.</div>
     </html>

Handler functions can return
[`FT`](https://www.fastht.ml/docs/explains/explaining_xt_components.html)
objects, which are automatically converted to HTML strings. The `FT`
class can take other `FT` components as arguments, such as `Div`. This
allows for easy composition of HTML elements in your responses.

``` python
@app.get
def autopost(): return Html(Div('Text.', hx_post=yoyo.to()))
print(cli.get('/autopost').text)
```

     <!doctype html>
     <html>
       <div hx-post="/yoyo">Text.</div>
     </html>

The `rt` decorator modifies the `yoyo` function by adding a `to()`
method. This method returns the route path associated with the handler.
It’s a convenient way to reference the route of a handler function
dynamically.

In the example, `yoyo.to()` is used as the value for `hx_post`. This
means when the div is clicked, it will trigger an HTMX POST request to
the route of the `yoyo` handler. This approach allows for flexible, DRY
code by avoiding hardcoded route strings and automatically updating if
the route changes.

This pattern is particularly useful in larger applications where routes
might change, or when building reusable components that need to
reference their own routes dynamically.

``` python
@app.get
def autoget(): return Html(Body(Div('Text.', cls='px-2', hx_post=show_host.to(a='b'))))
print(cli.get('/autoget').text)
```

     <!doctype html>
     <html>
       <body>
         <div hx-post="/hostie?a=b" class="px-2">Text.</div>
       </body>
     </html>

The `rt()` method of handler functions can also accept parameters. When
called with parameters, it returns the route path with a query string
appended. In this example, `show_host.to(a='b')` generates the path
`/hostie?a=b`.

The `Body` component is used here to demonstrate nesting of FT
components. `Div` is nested inside `Body`, showcasing how you can create
more complex HTML structures.

The `cls` parameter is used to add a CSS class to the `Div`. This
translates to the `class` attribute in the rendered HTML. (`class` can’t
be used as a parameter name directly in Python since it’s a reserved
word.)

``` python
@rt('/ft2')
def get(): return Title('Foo'),H1('bar')
print(cli.get('/ft2').text)
```

     <!doctype html>
     <html>
       <head>
         <title>Foo</title>
         <meta charset="utf-8">
         <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
    <script src="https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js"></script><script src="https://cdn.jsdelivr.net/gh/answerdotai/fasthtml-js@1.0.12/fasthtml.js"></script><script src="https://cdn.jsdelivr.net/gh/answerdotai/surreal@main/surreal.js"></script><script src="https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js"></script><script>
        function sendmsg() {
            window.parent.postMessage({height: document.documentElement.offsetHeight}, '*');
        }
        window.onload = function() {
            sendmsg();
            document.body.addEventListener('htmx:afterSettle',    sendmsg);
            document.body.addEventListener('htmx:wsAfterMessage', sendmsg);
        };</script>   </head>
       <body>
         <h1>bar</h1>
       </body>
     </html>

Handler functions can return multiple `FT` objects as a tuple. The first
item is treated as the `Title`, and the rest are added to the `Body`.
When the request is not an HTMX request, FastHTML automatically adds
necessary HTML boilerplate, including default `head` content with
required scripts.

When using `app.route` (or `rt`), if the function name matches an HTTP
verb (e.g., `get`,
[`post`](https://www.fastht.ml/docs/explains/stripe.html#post), `put`,
`delete`), that HTTP method is automatically used for the route. In this
case, a path must be explicitly provided as an argument to the
decorator.

``` python
hxhdr = {'headers':{'hx-request':"1"}}
print(cli.get('/ft2', **hxhdr).text)
```

     <title>Foo</title>
     <h1>bar</h1>

For HTMX requests (indicated by the `hx-request` header), FastHTML
returns only the specified components without the full HTML structure.
This allows for efficient partial page updates in HTMX applications.

``` python
@rt('/ft3')
def get(): return H1('bar')
print(cli.get('/ft3', **hxhdr).text)
```

     <h1>bar</h1>

When a handler function returns a single `FT` object for an HTMX
request, it’s rendered as a single HTML partial.

``` python
@rt('/ft4')
def get(): return Html(Head(Title('hi')), Body(P('there')))

print(cli.get('/ft4').text)
```

     <!doctype html>
     <html>
       <head>
         <title>hi</title>
       </head>
       <body>
         <p>there</p>
       </body>
     </html>

Handler functions can return a complete `Html` structure, including
`Head` and `Body` components. When a full HTML structure is returned,
FastHTML doesn’t add any additional boilerplate. This gives you full
control over the HTML output when needed.

``` python
@rt
def index(): return "welcome!"
print(cli.get('/').text)
```

    welcome!

The `index` function is a special handler in FastHTML. When defined
without arguments to the `@rt` decorator, it automatically becomes the
handler for the root path (`'/'`). This is a convenient way to define
the main page or entry point of your application.

## Path and Query Parameters

``` python
@rt('/user/{nm}', name='gday')
def get(nm:str=''): return f"Good day to you, {nm}!"
cli.get('/user/Alexis').text
```

    'Good day to you, Alexis!'

Handler functions can use path parameters, defined using curly braces in
the route – this is implemented by Starlette directly, so all Starlette
path parameters can be used. These parameters are passed as arguments to
the function.

The `name` parameter in the decorator allows you to give the route a
name, which can be used for URL generation.

In this example, `{nm}` in the route becomes the `nm` parameter in the
function. The function uses this parameter to create a personalized
greeting.

``` python
@app.get
def autolink(): return Html(Div('Text.', link=uri('gday', nm='Alexis')))
print(cli.get('/autolink').text)
```

     <!doctype html>
     <html>
       <div href="/user/Alexis">Text.</div>
     </html>

The [`uri`](https://www.fastht.ml/docs/api/core.html#uri) function is
used to generate URLs for named routes. It takes the route name as its
first argument, followed by any path or query parameters needed for that
route.

In this example, `uri('gday', nm='Alexis')` generates the URL for the
route named ‘gday’ (which we defined earlier as ‘/user/{nm}’), with
‘Alexis’ as the value for the ‘nm’ parameter.

The `link` parameter in FT components sets the `href` attribute of the
rendered HTML element. By using
[`uri()`](https://www.fastht.ml/docs/api/core.html#uri), we can
dynamically generate correct URLs even if the underlying route structure
changes.

This approach promotes maintainable code by centralizing route
definitions and avoiding hardcoded URLs throughout the application.

``` python
@rt('/link')
def get(req): return f"{req.url_for('gday', nm='Alexis')}; {req.url_for('show_host')}"

cli.get('/link').text
```

    'http://testserver/user/Alexis; http://testserver/hostie'

The `url_for` method of the request object can be used to generate URLs
for named routes. It takes the route name as its first argument,
followed by any path parameters needed for that route.

In this example, `req.url_for('gday', nm='Alexis')` generates the full
URL for the route named ‘gday’, including the scheme and host.
Similarly, `req.url_for('show_host')` generates the URL for the
‘show_host’ route.

This method is particularly useful when you need to generate absolute
URLs, such as for email links or API responses. It ensures that the
correct host and scheme are included, even if the application is
accessed through different domains or protocols.

``` python
app.url_path_for('gday', nm='Jeremy')
```

    '/user/Jeremy'

The `url_path_for` method of the application can be used to generate URL
paths for named routes. Unlike `url_for`, it returns only the path
component of the URL, without the scheme or host.

In this example, `app.url_path_for('gday', nm='Jeremy')` generates the
path ‘/user/Jeremy’ for the route named ‘gday’.

This method is useful when you need relative URLs or just the path
component, such as for internal links or when constructing URLs in a
host-agnostic manner.

``` python
@rt('/oops')
def get(nope): return nope
r = cli.get('/oops?nope=1')
print(r)
r.text
```

    <Response [200 OK]>

    /Users/iflath/git/AnswerDotAI/fasthtml/build/__editable__.python_fasthtml-0.12.1-py3-none-any/fasthtml/core.py:188: UserWarning: `nope has no type annotation and is not a recognised special name, so is ignored.
      if arg!='resp': warn(f"`{arg} has no type annotation and is not a recognised special name, so is ignored.")

    ''

Handler functions can include parameters, but they must be
type-annotated or have special names (like `req`) to be recognized. In
this example, the `nope` parameter is not annotated, so it’s ignored,
resulting in a warning.

When a parameter is ignored, it doesn’t receive the value from the query
string. This can lead to unexpected behavior, as the function attempts
to return `nope`, which is undefined.

The `cli.get('/oops?nope=1')` call succeeds with a 200 OK status because
the handler doesn’t raise an exception, but it returns an empty
response, rather than the intended value.

To fix this, you should either add a type annotation to the parameter
(e.g., `def get(nope: str):`) or use a recognized special name like
`req`.

``` python
@rt('/html/{idx}')
def get(idx:int): return Body(H4(f'Next is {idx+1}.'))
print(cli.get('/html/1', **hxhdr).text)
```

     <body>
       <h4>Next is 2.</h4>
     </body>

Path parameters can be type-annotated, and FastHTML will automatically
convert them to the specified type if possible. In this example, `idx`
is annotated as `int`, so it’s converted from the string in the URL to
an integer.

``` python
reg_re_param("imgext", "ico|gif|jpg|jpeg|webm")

@rt(r'/static/{path:path}{fn}.{ext:imgext}')
def get(fn:str, path:str, ext:str): return f"Getting {fn}.{ext} from /{path}"

print(cli.get('/static/foo/jph.ico').text)
```

    Getting jph.ico from /foo/

The
[`reg_re_param`](https://www.fastht.ml/docs/api/core.html#reg_re_param)
function is used to register custom path parameter types using regular
expressions. Here, we define a new path parameter type called “imgext”
that matches common image file extensions.

Handler functions can use complex path patterns with multiple parameters
and custom types. In this example, the route pattern
`r'/static/{path:path}{fn}.{ext:imgext}'` uses three path parameters:

1.  `path`: A Starlette built-in type that matches any path segments
2.  `fn`: The filename without extension
3.  `ext`: Our custom “imgext” type that matches specific image
    extensions

``` python
ModelName = str_enum('ModelName', "alexnet", "resnet", "lenet")

@rt("/models/{nm}")
def get(nm:ModelName): return nm

print(cli.get('/models/alexnet').text)
```

    alexnet

We define `ModelName` as an enum with three possible values: “alexnet”,
“resnet”, and “lenet”. Handler functions can use these enum types as
parameter annotations. In this example, the `nm` parameter is annotated
with `ModelName`, which ensures that only valid model names are
accepted.

When a request is made with a valid model name, the handler function
returns that name. This pattern is useful for creating type-safe APIs
with a predefined set of valid values.

``` python
@rt("/files/{path}")
async def get(path: Path): return path.with_suffix('.txt')
print(cli.get('/files/foo').text)
```

    foo.txt

Handler functions can use
[`Path`](https://www.fastht.ml/docs/api/svg.html#path) objects as
parameter types. The
[`Path`](https://www.fastht.ml/docs/api/svg.html#path) type is from
Python’s standard library `pathlib` module, which provides an
object-oriented interface for working with file paths. In this example,
the `path` parameter is annotated with
[`Path`](https://www.fastht.ml/docs/api/svg.html#path), so FastHTML
automatically converts the string from the URL to a
[`Path`](https://www.fastht.ml/docs/api/svg.html#path) object.

This approach is particularly useful when working with file-related
routes, as it provides a convenient and platform-independent way to
handle file paths.

``` python
fake_db = [{"name": "Foo"}, {"name": "Bar"}]

@rt("/items/")
def get(idx:int|None = 0): return fake_db[idx]
print(cli.get('/items/?idx=1').text)
```

    {"name":"Bar"}

Handler functions can use query parameters, which are automatically
parsed from the URL. In this example, `idx` is a query parameter with a
default value of 0. It’s annotated as `int|None`, allowing it to be
either an integer or None.

The function uses this parameter to index into a fake database
(`fake_db`). When a request is made with a valid `idx` query parameter,
the handler returns the corresponding item from the database.

``` python
print(cli.get('/items/').text)
```

    {"name":"Foo"}

When no `idx` query parameter is provided, the handler function uses the
default value of 0. This results in returning the first item from the
`fake_db` list, which is `{"name":"Foo"}`.

This behavior demonstrates how default values for query parameters work
in FastHTML. They allow the API to have a sensible default behavior when
optional parameters are not provided.

``` python
print(cli.get('/items/?idx=g'))
```

    <Response [404 Not Found]>

When an invalid value is provided for a typed query parameter, FastHTML
returns a 404 Not Found response. In this example, ‘g’ is not a valid
integer for the `idx` parameter, so the request fails with a 404 status.

This behavior ensures type safety and prevents invalid inputs from
reaching the handler function.

``` python
@app.get("/booly/")
def _(coming:bool=True): return 'Coming' if coming else 'Not coming'
print(cli.get('/booly/?coming=true').text)
print(cli.get('/booly/?coming=no').text)
```

    Coming
    Not coming

Handler functions can use boolean query parameters. In this example,
`coming` is a boolean parameter with a default value of `True`. FastHTML
automatically converts string values like ‘true’, ‘false’, ‘1’, ‘0’,
‘on’, ‘off’, ‘yes’, and ‘no’ to their corresponding boolean values.

The underscore `_` is used as the function name in this example to
indicate that the function’s name is not important or won’t be
referenced elsewhere. This is a common Python convention for throwaway
or unused variables, and it works here because FastHTML uses the route
decorator parameter, when provided, to determine the URL path, not the
function name. By default, both `get` and
[`post`](https://www.fastht.ml/docs/explains/stripe.html#post) methods
can be used in routes that don’t specify an http method (by either using
`app.get`, `def get`, or the `methods` parameter to `app.route`).

``` python
@app.get("/datie/")
def _(d:parsed_date): return d
date_str = "17th of May, 2024, 2p"
print(cli.get(f'/datie/?d={date_str}').text)
```

    2024-05-17 14:00:00

Handler functions can use `date` objects as parameter types. FastHTML
uses `dateutil.parser` library to automatically parse a wide variety of
date string formats into `date` objects.

``` python
@app.get("/ua")
async def _(user_agent:str): return user_agent
print(cli.get('/ua', headers={'User-Agent':'FastHTML'}).text)
```

    FastHTML

Handler functions can access HTTP headers by using parameter names that
match the header names. In this example, `user_agent` is used as a
parameter name, which automatically captures the value of the
‘User-Agent’ header from the request.

The [`Client`](https://www.fastht.ml/docs/api/core.html#client) instance
allows setting custom headers for test requests. Here, we set the
‘User-Agent’ header to ‘FastHTML’ in the test request.

``` python
@app.get("/hxtest")
def _(htmx): return htmx.request
print(cli.get('/hxtest', headers={'HX-Request':'1'}).text)

@app.get("/hxtest2")
def _(foo:HtmxHeaders, req): return foo.request
print(cli.get('/hxtest2', headers={'HX-Request':'1'}).text)
```

    1
    1

Handler functions can access HTMX-specific headers using either the
special `htmx` parameter name, or a parameter annotated with
[`HtmxHeaders`](https://www.fastht.ml/docs/api/core.html#htmxheaders).
Both approaches provide access to HTMX-related information.

In these examples, the `htmx.request` attribute returns the value of the
‘HX-Request’ header.

``` python
app.chk = 'foo'
@app.get("/app")
def _(app): return app.chk
print(cli.get('/app').text)
```

    foo

Handler functions can access the
[`FastHTML`](https://www.fastht.ml/docs/api/core.html#fasthtml)
application instance using the special `app` parameter name. This allows
handlers to access application-level attributes and methods.

In this example, we set a custom attribute `chk` on the application
instance. The handler function then uses the `app` parameter to access
this attribute and return its value.

``` python
@app.get("/app2")
def _(foo:FastHTML): return foo.chk,HttpHeader("mykey", "myval")
r = cli.get('/app2', **hxhdr)
print(r.text)
print(r.headers)
```

    foo
    Headers({'mykey': 'myval', 'content-length': '3', 'content-type': 'text/html; charset=utf-8'})

Handler functions can access the
[`FastHTML`](https://www.fastht.ml/docs/api/core.html#fasthtml)
application instance using a parameter annotated with
[`FastHTML`](https://www.fastht.ml/docs/api/core.html#fasthtml). This
allows handlers to access application-level attributes and methods, just
like using the special `app` parameter name.

Handlers can return tuples containing both content and
[`HttpHeader`](https://www.fastht.ml/docs/api/core.html#httpheader)
objects.
[`HttpHeader`](https://www.fastht.ml/docs/api/core.html#httpheader)
allows setting custom HTTP headers in the response.

In this example:

- We define a handler that returns both the `chk` attribute from the
  application and a custom header.
- The `HttpHeader("mykey", "myval")` sets a custom header in the
  response.
- We use the test client to make a request and examine both the response
  text and headers.
- The response includes the custom header “mykey” along with standard
  headers like content-length and content-type.

``` python
@app.get("/app3")
def _(foo:FastHTML): return HtmxResponseHeaders(location="http://example.org")
r = cli.get('/app3')
print(r.headers)
```

    Headers({'hx-location': 'http://example.org', 'content-length': '0', 'content-type': 'text/html; charset=utf-8'})

Handler functions can return
[`HtmxResponseHeaders`](https://www.fastht.ml/docs/api/core.html#htmxresponseheaders)
objects to set HTMX-specific response headers. This is useful for
HTMX-specific behaviors like client-side redirects.

In this example we define a handler that returns an
[`HtmxResponseHeaders`](https://www.fastht.ml/docs/api/core.html#htmxresponseheaders)
object with a `location` parameter, which sets the `HX-Location` header
in the response. HTMX uses this for client-side redirects.

``` python
@app.get("/app4")
def _(foo:FastHTML): return Redirect("http://example.org")
cli.get('/app4', follow_redirects=False)
```

    <Response [303 See Other]>

Handler functions can return
[`Redirect`](https://www.fastht.ml/docs/api/core.html#redirect) objects
to perform HTTP redirects. This is useful for redirecting users to
different pages or external URLs.

In this example:

- We define a handler that returns a
  [`Redirect`](https://www.fastht.ml/docs/api/core.html#redirect) object
  with the URL “http://example.org”.
- The `cli.get('/app4', follow_redirects=False)` call simulates a GET
  request to the ‘/app4’ route without following redirects.
- The response has a 303 See Other status code, indicating a redirect.

The `follow_redirects=False` parameter is used to prevent the test
client from automatically following the redirect, allowing us to inspect
the redirect response itself.

``` python
Redirect.__response__
```

    <function fasthtml.core.Redirect.__response__(self, req)>

The [`Redirect`](https://www.fastht.ml/docs/api/core.html#redirect)
class in FastHTML implements a `__response__` method, which is a special
method recognized by the framework. When a handler returns a
[`Redirect`](https://www.fastht.ml/docs/api/core.html#redirect) object,
FastHTML internally calls this `__response__` method to replace the
original response.

The `__response__` method takes a `req` parameter, which represents the
incoming request. This allows the method to access request information
if needed when constructing the redirect response.

``` python
@rt
def meta(): 
    return ((Title('hi'),H1('hi')),
        (Meta(property='image'), Meta(property='site_name')))

print(cli.post('/meta').text)
```

     <!doctype html>
     <html>
       <head>
         <title>hi</title>
         <meta property="image">
         <meta property="site_name">
         <meta charset="utf-8">
         <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
    <script src="https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js"></script><script src="https://cdn.jsdelivr.net/gh/answerdotai/fasthtml-js@1.0.12/fasthtml.js"></script><script src="https://cdn.jsdelivr.net/gh/answerdotai/surreal@main/surreal.js"></script><script src="https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js"></script><script>
        function sendmsg() {
            window.parent.postMessage({height: document.documentElement.offsetHeight}, '*');
        }
        window.onload = function() {
            sendmsg();
            document.body.addEventListener('htmx:afterSettle',    sendmsg);
            document.body.addEventListener('htmx:wsAfterMessage', sendmsg);
        };</script>   </head>
       <body>
         <h1>hi</h1>
       </body>
     </html>

FastHTML automatically identifies elements typically placed in the
`<head>` (like `Title` and `Meta`) and positions them accordingly, while
other elements go in the `<body>`.

In this example: - `(Title('hi'), H1('hi'))` defines the title and main
heading. The title is placed in the head, and the H1 in the body. -
`(Meta(property='image'), Meta(property='site_name'))` defines two meta
tags, which are both placed in the head.

## APIRouter

[`APIRouter`](https://www.fastht.ml/docs/api/core.html#apirouter) is
useful when you want to split your application routes across multiple
`.py` files that are part of a single FastHTMl application. It accepts
an optional `prefix` argument that will be applied to all routes within
that instance of
[`APIRouter`](https://www.fastht.ml/docs/api/core.html#apirouter).

Below we define several hypothetical product related routes in a
`products.py` and then demonstrate how they can seamlessly be
incorporated into a FastHTML app instance.

``` python
# products.py
ar = APIRouter(prefix="/products")

@ar("/all")
def all_products(req):
    return Div(
        "Welcome to the Products Page! Click the button below to look at the details for product 42",
        Div(
            Button(
                "Details",
                hx_get=req.url_for("details", pid=42),
                hx_target="#products_list",
                hx_swap="outerHTML",
            ),
        ),
        id="products_list",
    )


@ar.get("/{pid}", name="details")
def details(pid: int):
    return f"Here are the product details for ID: {pid}"
```

Since we specified the `prefix=/products` in our hypothetical
`products.py` file, all routes defined in that file will be found under
`/products`.

``` python
print(str(ar.rt_funcs.all_products))
print(str(ar.rt_funcs.details))
```

    /products/all
    /products/{pid}

``` python
# main.py
# from products import ar

app, rt = fast_app()
ar.to_app(app)

@rt
def index():
    return Div(
        "Click me for a look at our products",
        hx_get=ar.rt_funcs.all_products,
        hx_swap="outerHTML",
    )
```

Note how you can reference our python route functions via
`APIRouter.rt_funcs` in your `hx_{http_method}` calls like normal.

## Form Data and JSON Handling

``` python
app = FastHTML()
rt = app.route
cli = Client(app)
```

``` python
@app.post('/profile/me')
def profile_update(username: str): return username

r = cli.post('/profile/me', data={'username' : 'Alexis'}).text
assert r == 'Alexis'
print(r)
```

    Alexis

Handler functions can accept form data parameters, without needing to
manually extract it from the request. In this example, `username` is
expected to be sent as form data.

The `data` parameter in the `cli.post()` method simulates sending form
data in the request.

``` python
r = cli.post('/profile/me', data={})
assert r.status_code == 400
print(r.text)
r
```

    Missing required field: username

    <Response [400 Bad Request]>

If required form data is missing, FastHTML automatically returns a 400
Bad Request response with an error message.

``` python
@app.post('/pet/dog')
def pet_dog(dogname: str = None): return dogname or 'unknown name'
r = cli.post('/pet/dog', data={}).text
r
```

    'unknown name'

Handlers can have optional form data parameters with default values. In
this example, `dogname` is an optional parameter with a default value of
`None`.

Here, if the form data doesn’t include the `dogname` field, the function
uses the default value. The function returns either the provided
`dogname` or ‘unknown name’ if `dogname` is `None`.

``` python
@dataclass
class Bodie: a:int;b:str

@rt("/bodie/{nm}")
def post(nm:str, data:Bodie):
    res = asdict(data)
    res['nm'] = nm
    return res

print(cli.post('/bodie/me', data=dict(a=1, b='foo', nm='me')).text)
```

    {"a":1,"b":"foo","nm":"me"}

You can use dataclasses to define structured form data. In this example,
`Bodie` is a dataclass with `a` (int) and `b` (str) fields.

FastHTML automatically converts the incoming form data to a `Bodie`
instance where attribute names match parameter names. Other form data
elements are matched with parameters with the same names (in this case,
`nm`).

Handler functions can return dictionaries, which FastHTML automatically
JSON-encodes.

``` python
@app.post("/bodied/")
def bodied(data:dict): return data

d = dict(a=1, b='foo')
print(cli.post('/bodied/', data=d).text)
```

    {"a":"1","b":"foo"}

`dict` parameters capture all form data as a dictionary. In this
example, the `data` parameter is annotated with `dict`, so FastHTML
automatically converts all incoming form data into a dictionary.

Note that when form data is converted to a dictionary, all values become
strings, even if they were originally numbers. This is why the ‘a’ key
in the response has a string value “1” instead of the integer 1.

``` python
nt = namedtuple('Bodient', ['a','b'])

@app.post("/bodient/")
def bodient(data:nt): return asdict(data)
print(cli.post('/bodient/', data=d).text)
```

    {"a":"1","b":"foo"}

Handler functions can use named tuples to define structured form data.
In this example, `Bodient` is a named tuple with `a` and `b` fields.

FastHTML automatically converts the incoming form data to a `Bodient`
instance where field names match parameter names. As with the previous
example, all form data values are converted to strings in the process.

``` python
class BodieTD(TypedDict): a:int;b:str='foo'

@app.post("/bodietd/")
def bodient(data:BodieTD): return data
print(cli.post('/bodietd/', data=d).text)
```

    {"a":1,"b":"foo"}

You can use `TypedDict` to define structured form data with type hints.
In this example, `BodieTD` is a `TypedDict` with `a` (int) and `b` (str)
fields, where `b` has a default value of ‘foo’.

FastHTML automatically converts the incoming form data to a `BodieTD`
instance where keys match the defined fields. Unlike with regular
dictionaries or named tuples, FastHTML respects the type hints in
`TypedDict`, converting values to the specified types when possible
(e.g., converting ‘1’ to the integer 1 for the ‘a’ field).

``` python
class Bodie2:
    a:int|None; b:str
    def __init__(self, a, b='foo'): store_attr()

@app.post("/bodie2/")
def bodie(d:Bodie2): return f"a: {d.a}; b: {d.b}"
print(cli.post('/bodie2/', data={'a':1}).text)
```

    a: 1; b: foo

Custom classes can be used to define structured form data. Here,
`Bodie2` is a custom class with `a` (int|None) and `b` (str) attributes,
where `b` has a default value of ‘foo’. The `store_attr()` function
(from fastcore) automatically assigns constructor parameters to instance
attributes.

FastHTML automatically converts the incoming form data to a `Bodie2`
instance, matching form fields to constructor parameters. It respects
type hints and default values.

``` python
@app.post("/b")
def index(it: Bodie): return Titled("It worked!", P(f"{it.a}, {it.b}"))

s = json.dumps({"b": "Lorem", "a": 15})
print(cli.post('/b', headers={"Content-Type": "application/json", 'hx-request':"1"}, data=s).text)
```

     <title>It worked!</title>
    <main class="container">   <h1>It worked!</h1>
       <p>15, Lorem</p>
    </main>

Handler functions can accept JSON data as input, which is automatically
parsed into the specified type. In this example, `it` is of type
`Bodie`, and FastHTML converts the incoming JSON data to a `Bodie`
instance.

The [`Titled`](https://www.fastht.ml/docs/api/xtend.html#titled)
component is used to create a page with a title and main content. It
automatically generates an `<h1>` with the provided title, wraps the
content in a `<main>` tag with a “container” class, and adds a `title`
to the head.

When making a request with JSON data: - Set the “Content-Type” header to
“application/json” - Provide the JSON data as a string in the `data`
parameter of the request

## Cookies, Sessions, File Uploads, and more

``` python
@rt("/setcookie")
def get(): return cookie('now', datetime.now())

@rt("/getcookie")
def get(now:parsed_date): return f'Cookie was set at time {now.time()}'

print(cli.get('/setcookie').text)
time.sleep(0.01)
cli.get('/getcookie').text
```

    'Cookie was set at time 16:19:27.811570'

Handler functions can set and retrieve cookies. In this example:

- The `/setcookie` route sets a cookie named ‘now’ with the current
  datetime.
- The `/getcookie` route retrieves the ‘now’ cookie and returns its
  value.

The [`cookie()`](https://www.fastht.ml/docs/api/core.html#cookie)
function is used to create a cookie response. FastHTML automatically
converts the datetime object to a string when setting the cookie, and
parses it back to a date object when retrieving it.

``` python
cookie('now', datetime.now())
```

    HttpHeader(k='set-cookie', v='now="2025-01-30 16:19:29.997374"; Path=/; SameSite=lax')

The [`cookie()`](https://www.fastht.ml/docs/api/core.html#cookie)
function returns an
[`HttpHeader`](https://www.fastht.ml/docs/api/core.html#httpheader)
object with the ‘set-cookie’ key. You can return it in a tuple along
with `FT` elements, along with anything else FastHTML supports in
responses.

``` python
app = FastHTML(secret_key='soopersecret')
cli = Client(app)
rt = app.route
```

``` python
@rt("/setsess")
def get(sess, foo:str=''):
    now = datetime.now()
    sess['auth'] = str(now)
    return f'Set to {now}'

@rt("/getsess")
def get(sess): return f'Session time: {sess["auth"]}'

print(cli.get('/setsess').text)
time.sleep(0.01)

cli.get('/getsess').text
```

    Set to 2025-01-30 16:19:31.078650

    'Session time: 2025-01-30 16:19:31.078650'

Sessions store and retrieve data across requests. To use sessions, you
should to initialize the FastHTML application with a `secret_key`. This
is used to cryptographically sign the cookie used by the session.

The `sess` parameter in handler functions provides access to the session
data. You can set and get session variables using dictionary-style
access.

``` python
@rt("/upload")
async def post(uf:UploadFile): return (await uf.read()).decode()

with open('../../CHANGELOG.md', 'rb') as f:
    print(cli.post('/upload', files={'uf':f}, data={'msg':'Hello'}).text[:15])
```

    # Release notes

Handler functions can accept file uploads using Starlette’s `UploadFile`
type. In this example:

- The `/upload` route accepts a file upload named `uf`.
- The `UploadFile` object provides an asynchronous `read()` method to
  access the file contents.
- We use `await` to read the file content asynchronously and decode it
  to a string.

We added `async` to the handler function because it uses `await` to read
the file content asynchronously. In Python, any function that uses
`await` must be declared as `async`. This allows the function to be run
asynchronously, potentially improving performance by not blocking other
operations while waiting for the file to be read.

``` python
app.static_route('.md', static_path='../..')
print(cli.get('/README.md').text[:10])
```

    # FastHTML

The `static_route` method of the FastHTML application allows serving
static files with specified extensions from a given directory. In this
example:

- `.md` files are served from the `../..` directory (two levels up from
  the current directory).
- Accessing `/README.md` returns the contents of the README.md file from
  that directory.

``` python
help(app.static_route_exts)
```

    Help on method static_route_exts in module fasthtml.core:

    static_route_exts(prefix='/', static_path='.', exts='static') method of fasthtml.core.FastHTML instance
        Add a static route at URL path `prefix` with files from `static_path` and `exts` defined by `reg_re_param()`

``` python
app.static_route_exts()
assert cli.get('/README.txt').status_code == 404
print(cli.get('/README.txt').text[:50])
```

    404 Not Found

The `static_route_exts` method of the FastHTML application allows
serving static files with specified extensions from a given directory.
By default:

- It serves files from the current directory (‘.’).
- It uses the ‘static’ regex, which includes common static file
  extensions like ‘ico’, ‘gif’, ‘jpg’, ‘css’, ‘js’, etc.
- The URL prefix is set to ‘/’.

The ‘static’ regex is defined by FastHTML using this code:

``` python
reg_re_param("static", "ico|gif|jpg|jpeg|webm|css|js|woff|png|svg|mp4|webp|ttf|otf|eot|woff2|txt|html|map")
```

``` python
@rt("/form-submit/{list_id}")
def options(list_id: str):
    headers = {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'POST',
        'Access-Control-Allow-Headers': '*',
    }
    return Response(status_code=200, headers=headers)

print(cli.options('/form-submit/2').headers)
```

    Headers({'access-control-allow-origin': '*', 'access-control-allow-methods': 'POST', 'access-control-allow-headers': '*', 'content-length': '0', 'set-cookie': 'session_=eyJhdXRoIjogIjIwMjUtMDEtMzAgMTY6MTk6MzEuMDc4NjUwIn0=.Z5vtZA.1ooY2RCWopWAbLYDy6660g_LlHI; path=/; Max-Age=31536000; httponly; samesite=lax'})

FastHTML handlers can handle OPTIONS requests and set custom headers. In
this example:

- The `/form-submit/{list_id}` route handles OPTIONS requests.
- Custom headers are set to allow cross-origin requests (CORS).
- The function returns a Starlette `Response` object with a 200 status
  code and the custom headers.

You can return any Starlette Response type from a handler function,
giving you full control over the response when needed.

``` python
def _not_found(req, exc): return Div('nope')

app = FastHTML(exception_handlers={404:_not_found})
cli = Client(app)
rt = app.route

r = cli.get('/')
print(r.text)
```

     <!doctype html>
     <html>
       <head>
         <title>FastHTML page</title>
         <meta charset="utf-8">
         <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
    <script src="https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js"></script><script src="https://cdn.jsdelivr.net/gh/answerdotai/fasthtml-js@1.0.12/fasthtml.js"></script><script src="https://cdn.jsdelivr.net/gh/answerdotai/surreal@main/surreal.js"></script><script src="https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js"></script><script>
        function sendmsg() {
            window.parent.postMessage({height: document.documentElement.offsetHeight}, '*');
        }
        window.onload = function() {
            sendmsg();
            document.body.addEventListener('htmx:afterSettle',    sendmsg);
            document.body.addEventListener('htmx:wsAfterMessage', sendmsg);
        };</script>   </head>
       <body>
         <div>nope</div>
       </body>
     </html>

FastHTML allows you to define custom exception handlers – in this case,
a custom 404 (Not Found) handler function `_not_found`, which returns a
`Div` component with the text ‘nope’.</doc><doc title="Live Reloading" desc="Explanation of how to use live reloading for FastHTML development."># Live Reloading



When building your app it can be useful to view your changes in a web
browser as you make them. FastHTML supports live reloading which means
that it watches for any changes to your code and automatically refreshes
the webpage in your browser.

To enable live reloading simply replace
[`FastHTML`](https://www.fastht.ml/docs/api/core.html#fasthtml) in your
app with `FastHTMLWithLiveReload`.

``` python
from fasthtml.common import *
app = FastHTMLWithLiveReload()
```

Then in your terminal run `uvicorn` with reloading enabled.

    uvicorn main:app --reload

**⚠️ Gotchas** - A reload is only triggered when you save your
changes. - `FastHTMLWithLiveReload` should only be used during
development. - If your app spans multiple directories you might need to
use the `--reload-dir` flag to watch all files in each directory. See
the uvicorn [docs](https://www.uvicorn.org/settings/#development) for
more info. - The live reload script is only injected into the page when
rendering [ft
components](https://www.fastht.ml/docs/explains/explaining_xt_components.html).

## Live reloading with `fast_app`

In development the `fast_app` function provides the same functionality.
It instantiates the `FastHTMLWithLiveReload` class if you pass
`live=True`:

<div class="code-with-filename">

**main.py**

``` python
from fasthtml.common import *

app, rt = fast_app(live=True)

serve()
```

</div>

Line 3  
`fast_app()` instantiates the `FastHTMLWithLiveReload` class.

Line 5  
[`serve()`](https://www.fastht.ml/docs/api/core.html#serve) is a wrapper
around a `uvicorn` call.

To run `main.py` in live reload mode, just do `python main.py`. We
recommend turning off live reload when deploying your app to production.</doc></optional></project>
