Cacao
High-performance reactive web framework for Python.
Build dashboards, internal tools, and data apps with a simple API — real WebSocket reactivity, not full-page reruns.
Features · Quick Start · Examples · Components · Architecture · Static Builds · Contributing
v2.0 — Complete rewrite with signal-based reactivity, session-scoped state, plugin hooks, and a Streamlit-like API that doesn't re-run your entire script.
Quick Start
pip install cacao
import cacao as c
c.title("Hello, Cacao!")
c.text("Welcome to the simplest Python web framework")
cacao run app.py
That's it — 3 lines of Python and you have a live web app with hot reload.
Why Cacao?
| Feature | Cacao | Streamlit |
|---|---|---|
| Reactivity | Signal-based (only changed values update) | Full script re-run on every interaction |
| State | Session-scoped by design | st.session_state bolt-on |
| Updates | WebSocket streaming (instant) | HTTP polling (laggy) |
| Multi-user | Isolated sessions built-in | Shared state issues |
| API | Context managers + decorators | Magic globals |
| Static export | cacao build — no server needed |
Not supported |
Features
Reactive Signals
count = c.signal(0, name="count")
@c.on("increment")
async def increment(session, event):
count.set(session, count.get(session) + 1)
Signals are the core of Cacao. When a value changes, only the affected components update — no full-page reruns, no diffing your entire tree.
60+ Built-in Components
Layouts, forms, charts, display elements, navigation — everything you need out of the box.
Multi-Page Apps
with c.page("/"):
c.title("Home")
with c.page("/settings"):
c.title("Settings")
Hash-based routing works both in server mode and static builds.
Static Builds
cacao build app.py
Export your app to a static site (HTML + JS + CSS) that runs entirely in the browser. Deploy to GitHub Pages, Netlify, or any static host — no Python server required.
Plugin Hooks & Auth
Extensible architecture with plugin registration, authentication hooks, and notification toasts.
Dark & Light Themes
c.config(theme="dark") # or "light"
Examples
Interactive Counter
import cacao as c
c.config(title="Counter")
count = c.signal(0, name="count")
@c.on("increment")
async def increment(session, event):
count.set(session, count.get(session) + 1)
@c.on("decrement")
async def decrement(session, event):
count.set(session, count.get(session) - 1)
c.title("Counter")
with c.card():
c.metric("Count", count)
with c.row():
c.button("-", on_click="decrement", variant="secondary")
c.button("+", on_click="increment")
Dashboard with Charts
import cacao as c
c.config(title="Sales Dashboard", theme="dark")
sales = c.sample_sales_data()
c.title("Sales Dashboard")
with c.row():
c.metric("Revenue", "$45,231", trend="+20.1%", trend_direction="up")
c.metric("Orders", "1,247", trend="+12.5%", trend_direction="up")
c.metric("Customers", "842", trend="+5.3%", trend_direction="up")
with c.row():
with c.col(span=8):
with c.card("Revenue Trend"):
c.line(sales, x="date", y="revenue", area=True)
with c.col(span=4):
with c.card("By Category"):
c.pie(sales[:5], values="revenue", names="category", donut=True)
with c.card("Recent Transactions"):
c.table(sales[:10], columns=["date", "category", "revenue", "orders"])
More examples in the examples/ directory:
| Example | Description |
|---|---|
simple/hello.py |
Minimal hello world |
simple/counter.py |
Interactive counter with signals |
simple/metrics.py |
KPI dashboard |
simple/dashboard.py |
Full dashboard with charts |
chat/app.py |
Chat application |
todo/server.py |
Todo list app |
analytics_dashboard/ |
Analytics dashboard |
Components
Layout
| Component | Description |
|---|---|
row() |
Horizontal flex layout |
col(span=N) |
Column with 12-grid span |
grid() |
CSS grid layout |
container() |
Centered container |
stack() |
Vertical stack |
split() |
Side-by-side split |
hero() |
Hero section |
card() |
Card container |
tabs() / tab() |
Tabbed content |
accordion() |
Collapsible sections |
modal() |
Modal dialog |
panel() |
Sliding panel |
app_shell() |
Full app layout with sidebar |
Display
| Component | Description |
|---|---|
title() / text() |
Typography |
markdown() |
Markdown with optional TOC |
code() |
Syntax-highlighted code |
metric() |
KPI metric with trend |
table() |
Data table |
json_view() |
Interactive JSON viewer |
badge() / alert() |
Status indicators |
progress() |
Progress bar |
image() / video() |
Media |
timeline() |
Timeline display |
diff() |
Side-by-side diff viewer |
breadcrumb() |
Navigation breadcrumb |
Form
| Component | Description |
|---|---|
button() |
Button with variants |
input_field() |
Text input |
textarea() |
Multi-line input |
select() |
Dropdown select |
checkbox() / switch() |
Toggle inputs |
slider() |
Range slider |
date_picker() |
Date picker |
search_input() |
Search with suggestions |
file_upload() |
File upload |
chat() |
Chat input interface |
Charts
| Component | Description |
|---|---|
line() / area() |
Line and area charts |
bar() |
Bar chart |
pie() / donut() |
Pie and donut charts |
scatter() |
Scatter plot |
gauge() |
Gauge meter |
heatmap() |
Heatmap |
radar() |
Radar chart |
funnel() |
Funnel chart |
treemap() |
Treemap |
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ PYTHON SERVER │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │
│ │ Signals │ │ Sessions │ │ Events │ │ WebSocket/HTTP│ │
│ │ (state) │──│ (per- │──│ (typed │──│ Server │ │
│ │ │ │ client) │ │ async) │ │ (Starlette) │ │
│ └──────────┘ └──────────┘ └──────────┘ └───────┬───────┘ │
└────────────────────────────────────────────────────┼───────────┘
│ JSON
│ WebSocket
┌────────────────────────────────────────────────────┼───────────┐
│ REACT CLIENT │ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────┴──────┐ │
│ │ Hooks │ │Components│ │ Event │ │ State │ │
│ │useCacao()│──│ (60+ UI) │──│Dispatcher│──│ Store │ │
│ └──────────┘ └──────────┘ └──────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Data flow:
Python API → JSON definition → WebSocket → React renders
↑ ↓
Signal.set() ←── Event handler ←── User action
CLI
cacao run app.py # Run with hot reload
cacao run app.py --port 3000 # Custom port
cacao run app.py --no-reload # Disable hot reload
cacao create my-dashboard # Scaffold a new project
cacao build app.py # Build static site
cacao build app.py --base-path /repo # For GitHub Pages subdirectory
Static Builds
Cacao generates static sites that run entirely in the browser — no Python server required.
cacao build app.py
Output:
dist/
├── index.html # Your app
├── cacao.js # Runtime + built-in handlers
└── cacao.css # Styles
Built-in client-side handlers for common operations:
- Encoders — Base64, URL, HTML entities, JWT decode
- Generators — UUID, passwords, Lorem Ipsum
- Converters — JSON/YAML, case conversion, number bases
- Text — Statistics, regex testing
- Crypto — Hash generation, HMAC
Deploy to GitHub Pages:
# .github/workflows/deploy.yml
- run: pip install cacao
- run: cacao build app.py --base-path /${{ github.event.repository.name }}
See cacao-tools for a full static site example.
Tech Stack
| Layer | Technology |
|---|---|
| Backend | Python 3.10+, Starlette, Uvicorn, WebSocket |
| Frontend | React 18, Chart.js, LESS |
| Build | esbuild, hatchling |
| State | Signal-based reactivity with session scoping |
| Deployment | Static export, GitHub Pages, any ASGI server |
Project Structure
cacao/
├── __init__.py # Package exports
├── simple.py # Streamlit-like API (import cacao as c)
├── server/ # Core server
│ ├── ui.py # 60+ UI components
│ ├── chart.py # Chart components
│ ├── signal.py # Signal & Computed reactivity
│ ├── session.py # Session management
│ ├── events.py # Event handling
│ ├── server.py # HTTP + WebSocket server
│ └── data.py # Data utilities
├── cli/ # CLI (run, build, create)
├── frontend/ # Frontend source (devs only)
│ ├── src/components/ # React components
│ ├── src/styles/ # LESS styles
│ ├── src/handlers/ # Static build handlers
│ └── dist/ # Build output
└── examples/ # Example apps
Contributing
Contributions are welcome! See the repo issues for ideas.
fork → feature branch → commit → push → pull request
Metadata
Release files for cacao 2.0.19
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cacao-2.0.19.tar.gz | 163.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cacao-2.0.19-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 938.5 kB
Release files / cacao-2.0.19.tar.gz
| Download URL | cacao-2.0.19.tar.gz |
|---|---|
| Size | 163.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e0a6dc16439a0e98dd1f59bc417dbd49025b06bef40bd92bf262ab53b2adcb47
|
|
BLAKE2b-256 checksum How to use checksums |
2192709da584a842dcda00e32a4d2d6570921b09444e26462843058e84c7eee5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / cacao-2.0.19-py3-none-any.whl
| Download URL | cacao-2.0.19-py3-none-any.whl |
|---|---|
| Size | 775.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
87f81ff59208c3f40faca0bb82ad9be0bbcba0e4e3d03885412b4da65f31aa56
|
|
BLAKE2b-256 checksum How to use checksums |
6d6274d22c9ffa0d0d63597e17774573c789eacf68e4338eda2234887bbabbea
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|