Skip to main content

⚡ Velo

tests PyPI Python License: MIT Zero dependencies

Beautiful, reactive Python web apps in minutes. Zero dependencies.

Velo lets anyone turn a Python script into a polished web app, with dashboards, forms, data tools and chat UIs. It runs on your laptop and deploys anywhere with one command. The whole framework is about 140 KB of pure-stdlib Python plus a 16 KB (gzipped) client.

import velo

app = velo.App("Hello", accent="#6366f1")

@app.page("/")
def home(ui):
    name = ui.input("Your name", placeholder="Ada")
    n = ui.slider("How excited?", 1, 10, 7)
    ui.title(f"Hello {name or 'world'}" + " 🎉" * n)

app.run()
python app.py          # or:  velo run app.py

Dashboard


Why Velo?

Velo Typical script-rerun frameworks
Install size 0 dependencies, ~140 KB Dozens of transitive packages
What re-runs on a click Only the page function Often the whole script
UI updates DOM morphing: no flicker, focus and caret kept Partial or full re-paint
Hot reload Keeps your state (widgets and ui.state survive a save) Session usually resets
Syntax error while editing Overlay in the browser; app keeps running Varies
Tables Built-in search, sort and paging on the client Varies
Charts Built-in SVG line, area, bar and donut with tooltips, no deps Usually needs a plotting library
Typing in inputs Debounced; sliders throttled Often one round trip per change
Live push from Python app.push() from any thread (SSE) Limited
Deploy velo docker (vendors itself), honours $PORT Extra setup
Theme Light and dark with a toggle; one accent colour themes everything Config files

Install

pip install velo-ui
velo new myapp && cd myapp && velo run app.py

Needs Python 3.9+. Nothing else.


The 60-second tour

Pages and navigation

Two or more pages give you an automatic sidebar nav with client-side routing.

@app.page("/", title="Dashboard", icon="📊")
def dashboard(ui): ...

@app.page("/settings", title="Settings", icon="⚙️")
def settings(ui): ...

Widgets return values

name   = ui.input("Name", placeholder="…")          # also: type="password" | "email"
bio    = ui.textarea("Bio", rows=4)
age    = ui.number("Age", 30, min=0, max=120)
level  = ui.slider("Level", 0, 100, 50, suffix="%")
city   = ui.select("City", ["Karachi", "Lahore"])    # returns the object, any type
plan   = ui.radio("Plan", ["Free", "Pro", "Team"])   # segmented control
tags   = ui.multiselect("Tags", ["a", "b", "c"])     # chips
ok     = ui.checkbox("I agree");  dark = ui.toggle("Dark mode")
day    = ui.date("Start");        col  = ui.color("Brand", "#6366f1")
f      = ui.upload("CSV file", accept=".csv")        # UploadedFile(.name .data .text())
if ui.button("Save", icon="💾"): ...                 # True on the click run
ui.download("Export", data, "report.csv")

State and callbacks

@app.page("/")
def counter(ui):
    ui.init(count=0)                        # defaults, set once per browser session

    def add(step):
        ui.state.count += step

    with ui.row():
        ui.button("−", on_click=add, args=(-1,), kind="secondary")
        ui.button("+", on_click=add, args=(1,))
    ui.metric("Count", ui.state.count)

Callbacks run before the re-render, so the page always shows consistent state. Use ui.get("key") and ui.set("key", v) to read or write any widget that has a key=.

Layout

a, b, c = ui.columns(3)               # or ui.columns([2, 1])
a.metric("Revenue", "$12.4k", "+8%")  # containers proxy every component…
with b.card("Settings", "Optional subtitle"):   # …or use `with`
    ui.toggle("Enabled")
t1, t2 = ui.tabs(["Chart", "Data"])   # tabs switch instantly on the client
with ui.expander("Advanced"): ...
with ui.sidebar: ui.select("Region", [...])
with ui.row(): ...                    # horizontal button group

Data and charts (pandas optional)

ui.table(df)                                       # DataFrame, list of dicts, dict of lists…
ui.line_chart(df, x="date", y=["sales", "cost"])
ui.area_chart(rows, x="day", y="revenue", title="Revenue")
ui.bar_chart(rows, x="day", y=["a", "b"], stacked=True)
ui.pie_chart({"k": ["A", "B"], "v": [60, 40]})
ui.metric("Users", 1520, delta=-12, inverse=True)  # inverse: down is good

Text and feedback

ui.title, ui.header, ui.subheader, ui.text, ui.caption, ui.markdown (safe), ui.code, ui.json, ui.badge, ui.link, ui.image (URL, path, bytes or PIL), ui.progress, ui.info, ui.success, ui.warning, ui.error, ui.toast, ui.divider, ui.html (trusted).

Chat apps

ui.init(msgs=[])
if q := ui.chat_input("Ask me…"):
    ui.state.msgs += [("user", q), ("assistant", my_llm(q))]
for role, text in ui.state.msgs:
    ui.chat_message(role, text)

Speed tools

@velo.cache(ttl=300)          # shared across sessions, TTL + LRU
def load_data(path): ...

ui.autorefresh(5)             # live dashboards: re-run every 5 s while visible
app.push()                    # from a background thread: refresh all open browsers
app.push(ui.session_id)       # …or just one
ui.rerun(); ui.navigate("/other")

Deploy

velo run app.py --prod          # 0.0.0.0, no reload, no tracebacks, gzip on
velo docker app.py              # writes Dockerfile and vendors velo
docker build -t myapp . && docker run -p 8000:8000 myapp

Velo reads $PORT and $VELO_HOST, so the same app runs unchanged on Render, Railway, Fly.io, Cloud Run, Koyeb, Heroku or a VPS. /health is there for load balancers. Behind nginx, turn off buffering for /api/stream (Velo already sends X-Accel-Buffering: no).

CLI

Command What it does
velo run app.py [--port N] [--host H] [--open] [--prod] [--no-reload] Run an app
velo new NAME Scaffold a starter app
velo docker [app.py] [--port N] Generate a Dockerfile and vendor Velo
velo version Print the version

How it works

  1. The browser opens one SSE stream (used for hot reload and server push) and sends small JSON events (set, click, submit) to /api/render.
  2. The server applies the event to that browser's session, runs callbacks, then re-runs only the page function. That builds a compact component tree.
  3. The client morphs the live DOM to match the tree. Unchanged nodes aren't touched, so focus, caret, scroll position, open tabs and table sort all survive.
  4. When you save a file, the reloader re-executes your module, swaps the page functions into the running app and tells browsers to re-render. Sessions stay where they were.

Customising the look

Pass App(accent="#10b981", theme="dark", wide=True, icon="🚀"), or override any CSS variable (--accent, --radius, --font…) with ui.html("<style>:root{--r:6px}</style>").

Current limits

  • Sessions live in memory: use one process per app, or sticky sessions if you scale out.
  • Uploads travel as base64 JSON (practical up to roughly 25 MB).
  • No streaming of partial output inside a single run yet. For long jobs, use a background thread with app.push().

Testing

python -m pytest -q        # or: python tests/test_velo.py

Contributing

Contributions are very welcome. Start with CONTRIBUTING.md, pick a good first issue, and say hi in Discussions. Please follow the Code of Conduct.

MIT licensed. © 2026 Muhammad Rafay Shaikh

Metadata

Release files for velo-ui 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for velo-ui 0.1.0
File Size Uploaded Provenance Actions
velo_ui-0.1.0.tar.gz 45.7 kB With provenance Details for velo_ui-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for velo-ui 0.1.0
File Interpreter ABI Platform Actions
velo_ui-0.1.0-py3-none-any.whl Python 3 none any Details for velo_ui-0.1.0-py3-none-any.whl

Total release size: 87.8 kB

Release files / velo_ui-0.1.0.tar.gz

Download URL velo_ui-0.1.0.tar.gz
Size 45.7 kB
Tags Source
SHA-256 checksum
How to use checksums
3aaa1e64f696ff7ba3d4c6b873b1d889d450bc1b936a2ba580c5fece0396de38
BLAKE2b-256 checksum
How to use checksums
c2e27ec6a5764c21a2758f7357f9944ae12dd9f0d715e4a735af1d8fc4ce3b2e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

Release files / velo_ui-0.1.0-py3-none-any.whl

Download URL velo_ui-0.1.0-py3-none-any.whl
Size 42.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9fa6e16975574968a89f9c17b95c5adf0e81679649379435d5ea027e72feeca6
BLAKE2b-256 checksum
How to use checksums
830ae9b01cda38ed286c6dafeb9474780f300c81eff8b4948e6016028e790e82
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

Security for velo-ui 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for velo_ui-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page