GitHub

Quickstart

To try PyWeb without installing anything, open the playground: it runs the compiler, server rendering and your @server functions in the browser.

#Install

The package is published as pyweb-stack; you import it as pyweb and the command is pyweb.

Terminal
pip install pyweb-stack            # Python 3.10+; no other dependencies
pip install "pyweb-stack[all]"     # optional: Postgres, MySQL, Redis, cryptography, uvicorn

#Create and run an app

Terminal
pyweb new hello
cd hello
pyweb dev app.pyweb          # http://localhost:8000, reloads on save

pyweb new writes app.pyweb, a starter test (test_app.py) and AGENTS.md/CLAUDE.md (instructions for AI coding agents). Add --template todo (or blog, auth, chat, blank) to start from a bigger example. The default counter app is:

app.pyweb
from pyweb import App

app = App(title="Hello")


@app.page("/")
def Home():
    count = 0

    def increment():
        count += 1

    <main>
        <h1>Counter</h1>
        <button onclick={increment}>
            Count: {count}
        </button>
    </main>
What this compiles to
JavaScript
// Home.js
function Home($s) {
  const count = $signal("count" in $s ? $s["count"] : 0);
  function increment() {
    count($py.add(count(), 1));
  }
  return [$h("main", null, () => [
      $h("h1", null, () => [$t("Counter")]),
      $h("button", {"onclick": increment}, () => [
        $t("Count: "),
        $dyn(() => count())
      ])
    ])];
}
$mount("Home", Home);

What happened:

  • count is read by the markup and changed by increment, so it became a signal. Clicking the button updates only that button's text.
  • increment was compiled to a JavaScript function. Nothing is sent to the server when you click.
  • The first response was complete HTML (Count: 0), rendered on the server.

Edit the file and save: the dev server recompiles and the browser reloads. If you introduce an error, the page shows it with the line.

#See what the compiler decided

Terminal
pyweb inspect app.pyweb
Output
page Home("/") signals ['count'] rpc []
  place count: browser  # reactive state: assigned in increment(); literal initial value
  place increment: browser  # event handler (compiled to JavaScript)

#Check, build and serve

Terminal
pyweb check app.pyweb                          # compile + security checks (CI-friendly)
pyweb build app.pyweb --out dist --production  # hashed, minified, deployable dist/
pyweb serve dist                               # production server with /healthz

dist/ contains your app source, static assets and a Dockerfile; see Deployment.

#Next

The tutorial builds a notes app with a database, server functions and login. Then, depending on what you're building:

Edit this page on GitHub