Build Secure Web Apps
in Pure Python

Vaadin for Python: the easy, secure web framework with thin client architecture.
No JavaScript. No exposed APIs. 50+ enterprise-grade Vaadin components.

hello.py

    

Why PyXFlow?

An easy Python web framework with built-in security. Everything you need, without leaving Python.

Pure Python

Write your entire UI in Python. No JavaScript, no HTML templates, no CSS. Just clean, idiomatic Python code.

50+ Vaadin Components

Enterprise-grade UI components: Grid, Forms, Charts, Login, MenuBar, TreeGrid, and many more. Battle-tested at scale.

Real-time Push

Built-in WebSocket support for live updates. Stream data to the browser in real time with server push.

Hot Reload

Instant feedback with --dev mode. Edit your Python code and see changes reflected immediately in the browser.

Secure by Design

Thin client architecture means no business logic in the browser. No REST APIs to exploit, no endpoints to attack. Security built in, not bolted on.

Easy to Learn

If you know Python, you already know PyXFlow. No frontend toolchain, no webpack, no npm. Just pip install and start building.

How It Works

Your code runs on the server. The browser is just a thin client.
No APIs to expose, no JS business code to ship. Secure web development by default.

Server-side architecture: Python server connected to thin-client browsers via WebSocket

Python Server

  • Business logic
  • Data access & validation
  • UI state management
  • Session & security
  • Your Python code
UIDL/WebSocket
UI diffs only

Browser (Thin Client)

  • Standard web components
  • DOM rendering
  • No business logic
  • No data access
  • No API endpoints

Zero Attack Surface

No REST APIs, no GraphQL, no endpoints to exploit. The UIDL protocol is opaque to pentesters. XSS and injection are by design impossible.

Server-side State

All logic, validation, and data stay on the server. Nothing sensitive reaches the browser. Dificult to tamper from DevTools.

Minimal Bandwidth

Only UI diffs travel the wire. No full-page reloads, no heavy JS bundles to download. Instant interactions over WebSocket.

See It in Action

Real examples, real screenshots. From hello world to full CRUD apps.

Hello World

A complete web app in Python. Text field, button, and a notification toast.

views/hello.py
from pyxflow import Route, Menu
from pyxflow.components import *

@Route("hello")
@Menu("Hello", icon="vaadin:hand")
class HelloView(HorizontalLayout):
    def __init__(self):
        name = TextField("Your name")
        button = Button("Say hello", lambda e:
            Notification.show(f"Hello {name.value}"))
        self.add(name, button)
Hello World demo

Data Grid with Lazy Loading

Sortable columns, lazy data provider, selection events. Fetches only visible rows.

views/grid.py
@Route("grid")
@Menu("Grid", icon="vaadin:table")
class GridView(VerticalLayout):
    def __init__(self):
        self.grid = Grid()
        self.grid.add_column("name", header="Name")
        self.grid.add_column("email", header="Email")
        self.grid.add_column("role").set_sortable(True)
        self.grid.add_column("city").set_sortable(True)

        # Lazy: only fetches visible rows
        self.grid.set_data_provider(self.fetch)
        self.add(self.grid)

    def fetch(self, offset, limit, sorts):
        return people[offset:offset+limit], len(people)
Data Grid demo

Master-Detail CRUD

SplitLayout with Grid + Form. Binder handles validation and save/cancel.

views/master_detail.py
from pyxflow.components import *
from pyxflow.data import Binder

@Route("master-detail")
@Menu("Master-Detail", icon="vaadin:split-h")
class MasterDetailView(Div):
    def __init__(self):
        split = SplitLayout()
        self.grid = Grid()
        self.grid.set_columns("firstName", "lastName",
            "email", "phone")
        self.grid.set_items(people)
        split.add_to_primary(self.grid)

        form = FormLayout()
        self.first = TextField("First Name")
        self.last = TextField("Last Name")
        form.add(self.first, self.last)

        self.binder = Binder(Person)
        self.binder.bind_instance_fields(self)
        split.add_to_secondary(form)
        self.add(split)
Master-Detail demo

Quick Start

Up and running in under a minute.

# Install PyXFlow
$ pip install pyxflow

# Create your app
$ mkdir myapp && cd myapp
$ mkdir views

# Optionally create some demo views
$ pyxflow --setup

# Run with hot reload
$ pyxflow --dev
myapp/
  views/ ← your views
    hello.py
  static/ ← optional
    styles/styles.css ← custom styles