Session State
use_state gives each visitor a tiny amount of state that
lives on the server for the life of their session. No database, no
config.
The idea#
Koyo is static-first, but a visit counter or a theme toggle needs a little per-user memory. Sessions are the unit of that memory: every visitor gets their own isolated store that Koyo tracks with an httponly cookie. State is keyed per session, so two tabs from the same browser share a session while two different visitors never do.
The API#
use_state is a module-level function. Pass the request,
a key, and an initial value. It returns a pair: a current value and
an action object:
# components/counter.py
from koyoapp.html import button, div, p, span
from koyoapp.state import use_state
def visit_counter(request):
count, count_action = use_state(request, "visit_count", 0)
return div(class_="card", id="visit-card")[
p("Session state, live right here"),
div(class_="flex items-center gap-3")[
span(id="visit-count")[f"Visits: {count}"],
button(class_="btn", hx_target="#visit-card", hx_swap="outerHTML",
**count_action.increment(by=1))["+1"],
],
]
The action object changes the value through two helpers:
action.increment(by=1)adds to the current value.action.set(value)replaces it.
Spraying the action onto the button (**action.increment(by=1))
fills in its hx_post URL automatically, and the
hx_target plus hx_swap attributes swap in a
fresh render of the card. A component like this lives in the scaffold's
components/counter.py and is rendered by the home page.
How it works#
-
Mutations are delivered as POST requests to an internal Koyo route
(
/__koyo_state/<token>); you never build those URLs yourself. -
The session cookie is named
koyo_session, httponly, SameSite=Lax, and scoped to the site path. - Sessions expire after 30 minutes of inactivity and are recreated on the next visit.
Limitations#
State is in memory only. It is lost when the server restarts, it is not shared across processes or deployments, and it is not a database. Use it for a visit counter or a preference, not for carts, accounts, or anything that must survive a redeploy.
- No disk or database backend ships with Koyo today.
- A single server process holds all active sessions.
- Two separate browser sessions do not share state.
State during the production build#
koyoapp build prerenders pages without a live request, so
request is None and there is no session to
read. use_state returns the initial value in that case.
The result is safe static HTML: a static build always renders the
defaults, never a private count.